diff --git a/.buildkite/ftr_configs.yml b/.buildkite/ftr_configs.yml index ac53265b1c979..052f6623dc1a6 100644 --- a/.buildkite/ftr_configs.yml +++ b/.buildkite/ftr_configs.yml @@ -63,6 +63,7 @@ disabled: - x-pack/plugins/observability_solution/synthetics/e2e/synthetics/synthetics_run.ts - x-pack/plugins/observability_solution/exploratory_view/e2e/synthetics_run.ts - x-pack/plugins/observability_solution/ux/e2e/synthetics_run.ts + - x-pack/plugins/observability_solution/slo/e2e/synthetics_run.ts # Configs that exist but weren't running in CI when this file was introduced - x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/config.ts @@ -321,6 +322,7 @@ enabled: - x-pack/test/functional/apps/saved_query_management/config.ts - x-pack/test/functional/apps/security/config.ts - x-pack/test/functional/apps/slo/embeddables/config.ts + - x-pack/test/functional/apps/search_playground/config.ts - x-pack/test/functional/apps/snapshot_restore/config.ts - x-pack/test/functional/apps/spaces/config.ts - x-pack/test/functional/apps/spaces/in_solution_navigation/config.ts @@ -403,7 +405,9 @@ enabled: - x-pack/test/security_solution_endpoint/endpoint.config.ts - x-pack/test/security_solution_endpoint/serverless.endpoint.config.ts - x-pack/test/security_solution_endpoint/integrations.config.ts + - x-pack/test/security_solution_endpoint/integrations_feature_flag.config.ts - x-pack/test/security_solution_endpoint/serverless.integrations.config.ts + - x-pack/test/security_solution_endpoint/serverless.integrations_feature_flag.config.ts - x-pack/test/session_view/basic/config.ts - x-pack/test/spaces_api_integration/security_and_spaces/config_basic.ts - x-pack/test/spaces_api_integration/security_and_spaces/copy_to_space_config_basic.ts diff --git a/.buildkite/pipelines/pull_request/slo_plugin_e2e.yml b/.buildkite/pipelines/pull_request/slo_plugin_e2e.yml new file mode 100644 index 0000000000000..83332811bcc20 --- /dev/null +++ b/.buildkite/pipelines/pull_request/slo_plugin_e2e.yml @@ -0,0 +1,17 @@ +steps: + - command: .buildkite/scripts/steps/functional/slo_plugin_e2e.sh + label: 'SLO Plugin @elastic/synthetics Tests' + agents: + queue: n2-4-spot + depends_on: + - build + - quick_checks + timeout_in_minutes: 30 + artifact_paths: + - 'x-pack/plugins/observability_solution/slo/e2e/.journeys/**/*' + retry: + automatic: + - exit_status: '-1' + limit: 3 + - exit_status: '*' + limit: 1 diff --git a/.buildkite/pipelines/quality-gates/pipeline.kibana-tests.yaml b/.buildkite/pipelines/quality-gates/pipeline.kibana-tests.yaml index c24a1d504cd82..6fa4ddf634524 100644 --- a/.buildkite/pipelines/quality-gates/pipeline.kibana-tests.yaml +++ b/.buildkite/pipelines/quality-gates/pipeline.kibana-tests.yaml @@ -26,7 +26,7 @@ steps: QG_PIPELINE_LOCATION: ".buildkite/pipelines/quality-gates" command: "make -C /agent run-environment-tests" # will trigger https://buildkite.com/elastic/kibana-tests agents: - image: "docker.elastic.co/ci-agent-images/quality-gate-seedling:0.0.2" + image: "docker.elastic.co/ci-agent-images/quality-gate-seedling:0.0.4" notify: - slack: "${TEAM_CHANNEL?}" diff --git a/.buildkite/scripts/pipelines/pull_request/pipeline.ts b/.buildkite/scripts/pipelines/pull_request/pipeline.ts index 13b89678ccac6..db0e8239d9701 100644 --- a/.buildkite/scripts/pipelines/pull_request/pipeline.ts +++ b/.buildkite/scripts/pipelines/pull_request/pipeline.ts @@ -139,6 +139,10 @@ const uploadPipeline = (pipelineContent: string | object) => { pipeline.push(getPipeline('.buildkite/pipelines/pull_request/ux_plugin_e2e.yml')); } + if (await doAnyChangesMatch([/^x-pack\/plugins\/observability_solution/])) { + pipeline.push(getPipeline('.buildkite/pipelines/pull_request/slo_plugin_e2e.yml')); + } + if ( GITHUB_PR_LABELS.includes('ci:deploy-cloud') || GITHUB_PR_LABELS.includes('ci:cloud-deploy') || diff --git a/.buildkite/scripts/steps/functional/slo_plugin_e2e.sh b/.buildkite/scripts/steps/functional/slo_plugin_e2e.sh new file mode 100755 index 0000000000000..95007fbff85bf --- /dev/null +++ b/.buildkite/scripts/steps/functional/slo_plugin_e2e.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +set -euo pipefail + +source .buildkite/scripts/common/util.sh + +.buildkite/scripts/bootstrap.sh +.buildkite/scripts/download_build_artifacts.sh + +export JOB=kibana-ux-plugin-synthetics + +echo "--- SLO @elastic/synthetics Tests" + +cd "$XPACK_DIR" + +node plugins/observability_solution/slo/scripts/e2e.js --kibana-install-dir "$KIBANA_BUILD_LOCATION" ${GREP:+--grep \"${GREP}\"} diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 726e9c2223d39..d160dda238f8d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -390,6 +390,7 @@ src/plugins/embeddable @elastic/kibana-presentation x-pack/examples/embedded_lens_example @elastic/kibana-visualizations x-pack/plugins/encrypted_saved_objects @elastic/kibana-security x-pack/plugins/enterprise_search @elastic/enterprise-search-frontend +x-pack/packages/kbn-entities-schema @elastic/obs-knowledge-team examples/error_boundary @elastic/appex-sharedux packages/kbn-es @elastic/kibana-operations packages/kbn-es-archiver @elastic/kibana-operations @elastic/appex-qa @@ -1278,6 +1279,10 @@ x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts @elastic/kib /src/dev/eslint/security_eslint_rule_tests.ts @elastic/kibana-security /src/core/server/integration_tests/config/check_dynamic_config.test.ts @elastic/kibana-security /src/plugins/telemetry/server/config/telemetry_labels.ts @elastic/kibana-security +/packages/kbn-std/src/is_internal_url.test.ts @elastic/kibana-core @elastic/kibana-security +/packages/kbn-std/src/is_internal_url.ts @elastic/kibana-core @elastic/kibana-security +/packages/kbn-std/src/parse_next_url.test.ts @elastic/kibana-core @elastic/kibana-security +/packages/kbn-std/src/parse_next_url.ts @elastic/kibana-core @elastic/kibana-security /test/interactive_setup_api_integration/ @elastic/kibana-security /test/interactive_setup_functional/ @elastic/kibana-security /test/plugin_functional/test_suites/core_plugins/rendering.ts @elastic/kibana-security diff --git a/api_docs/actions.devdocs.json b/api_docs/actions.devdocs.json index 351e17acc532c..621d8237c0503 100644 --- a/api_docs/actions.devdocs.json +++ b/api_docs/actions.devdocs.json @@ -2429,6 +2429,39 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "actions", + "id": "def-server.getBasicAuthHeader", + "type": "Function", + "tags": [], + "label": "getBasicAuthHeader", + "description": [], + "signature": [ + "({ username, password }: GetBasicAuthHeaderArgs) => { Authorization: string; }" + ], + "path": "x-pack/plugins/actions/server/lib/get_basic_auth_header.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "actions", + "id": "def-server.getBasicAuthHeader.$1", + "type": "Object", + "tags": [], + "label": "{ username, password }", + "description": [], + "signature": [ + "GetBasicAuthHeaderArgs" + ], + "path": "x-pack/plugins/actions/server/lib/get_basic_auth_header.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "actions", "id": "def-server.urlAllowListValidator", diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index 6d677b0619e15..0d8591b85a84c 100644 --- a/api_docs/actions.mdx +++ b/api_docs/actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/actions title: "actions" image: https://source.unsplash.com/400x175/?github description: API docs for the actions plugin -date: 2024-05-16 +date: 2024-05-20 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 | |-------------------|-----------|------------------------|-----------------| -| 301 | 0 | 295 | 32 | +| 303 | 0 | 297 | 32 | ## Client diff --git a/api_docs/advanced_settings.mdx b/api_docs/advanced_settings.mdx index bc6c6c7e388fa..e9a421e888be2 100644 --- a/api_docs/advanced_settings.mdx +++ b/api_docs/advanced_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/advancedSettings title: "advancedSettings" image: https://source.unsplash.com/400x175/?github description: API docs for the advancedSettings plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] --- import advancedSettingsObj from './advanced_settings.devdocs.json'; diff --git a/api_docs/ai_assistant_management_selection.mdx b/api_docs/ai_assistant_management_selection.mdx index f04c826eef9a3..e02245146f91b 100644 --- a/api_docs/ai_assistant_management_selection.mdx +++ b/api_docs/ai_assistant_management_selection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiAssistantManagementSelection title: "aiAssistantManagementSelection" image: https://source.unsplash.com/400x175/?github description: API docs for the aiAssistantManagementSelection plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiAssistantManagementSelection'] --- import aiAssistantManagementSelectionObj from './ai_assistant_management_selection.devdocs.json'; diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index e73af6333db7e..7ecce04ce1e65 100644 --- a/api_docs/aiops.mdx +++ b/api_docs/aiops.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiops title: "aiops" image: https://source.unsplash.com/400x175/?github description: API docs for the aiops plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiops'] --- import aiopsObj from './aiops.devdocs.json'; diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx index 9903181b6fa6a..b28eb1bfdaf88 100644 --- a/api_docs/alerting.mdx +++ b/api_docs/alerting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/alerting title: "alerting" image: https://source.unsplash.com/400x175/?github description: API docs for the alerting plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'alerting'] --- import alertingObj from './alerting.devdocs.json'; diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index b2b1e56b77543..2c3da0893ff2a 100644 --- a/api_docs/apm.mdx +++ b/api_docs/apm.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apm title: "apm" image: https://source.unsplash.com/400x175/?github description: API docs for the apm plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; diff --git a/api_docs/apm_data_access.mdx b/api_docs/apm_data_access.mdx index 6ed0b95daa8ad..f50b0eea77370 100644 --- a/api_docs/apm_data_access.mdx +++ b/api_docs/apm_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apmDataAccess title: "apmDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the apmDataAccess plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apmDataAccess'] --- import apmDataAccessObj from './apm_data_access.devdocs.json'; diff --git a/api_docs/asset_manager.mdx b/api_docs/asset_manager.mdx index b872e815d58a7..b0ff0227b6d01 100644 --- a/api_docs/asset_manager.mdx +++ b/api_docs/asset_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/assetManager title: "assetManager" image: https://source.unsplash.com/400x175/?github description: API docs for the assetManager plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'assetManager'] --- import assetManagerObj from './asset_manager.devdocs.json'; diff --git a/api_docs/assets_data_access.mdx b/api_docs/assets_data_access.mdx index 43ad957a1dbd6..401513ef27e18 100644 --- a/api_docs/assets_data_access.mdx +++ b/api_docs/assets_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/assetsDataAccess title: "assetsDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the assetsDataAccess plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'assetsDataAccess'] --- import assetsDataAccessObj from './assets_data_access.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index 63df940e00978..31fbbf7e94578 100644 --- a/api_docs/banners.mdx +++ b/api_docs/banners.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/banners title: "banners" image: https://source.unsplash.com/400x175/?github description: API docs for the banners plugin -date: 2024-05-16 +date: 2024-05-20 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 627a850f3d091..bba0e4c29bab6 100644 --- a/api_docs/bfetch.mdx +++ b/api_docs/bfetch.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/bfetch title: "bfetch" image: https://source.unsplash.com/400x175/?github description: API docs for the bfetch plugin -date: 2024-05-16 +date: 2024-05-20 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 5a1e7b6f469a8..24ed742c77f02 100644 --- a/api_docs/canvas.mdx +++ b/api_docs/canvas.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/canvas title: "canvas" image: https://source.unsplash.com/400x175/?github description: API docs for the canvas plugin -date: 2024-05-16 +date: 2024-05-20 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 9a483c551c21a..1c3da2e8f7236 100644 --- a/api_docs/cases.mdx +++ b/api_docs/cases.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cases title: "cases" image: https://source.unsplash.com/400x175/?github description: API docs for the cases plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cases'] --- import casesObj from './cases.devdocs.json'; diff --git a/api_docs/charts.mdx b/api_docs/charts.mdx index f4e7cb47fcbe8..1307d78b19c43 100644 --- a/api_docs/charts.mdx +++ b/api_docs/charts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/charts title: "charts" image: https://source.unsplash.com/400x175/?github description: API docs for the charts plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'charts'] --- import chartsObj from './charts.devdocs.json'; diff --git a/api_docs/cloud.mdx b/api_docs/cloud.mdx index 6aec0b9d434c3..ae1f4c9893e4f 100644 --- a/api_docs/cloud.mdx +++ b/api_docs/cloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloud title: "cloud" image: https://source.unsplash.com/400x175/?github description: API docs for the cloud plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; diff --git a/api_docs/cloud_data_migration.mdx b/api_docs/cloud_data_migration.mdx index 67c7128daecdf..3c32678ea6a9d 100644 --- a/api_docs/cloud_data_migration.mdx +++ b/api_docs/cloud_data_migration.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDataMigration title: "cloudDataMigration" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDataMigration plugin -date: 2024-05-16 +date: 2024-05-20 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 3d7d64c78461a..8de28a174848f 100644 --- a/api_docs/cloud_defend.mdx +++ b/api_docs/cloud_defend.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDefend title: "cloudDefend" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDefend plugin -date: 2024-05-16 +date: 2024-05-20 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 f9e1df34e94a2..e6e64081f4816 100644 --- a/api_docs/cloud_experiments.mdx +++ b/api_docs/cloud_experiments.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudExperiments title: "cloudExperiments" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudExperiments plugin -date: 2024-05-16 +date: 2024-05-20 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 0413b5651fbb2..d3be4f1ecfc58 100644 --- a/api_docs/cloud_security_posture.mdx +++ b/api_docs/cloud_security_posture.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudSecurityPosture title: "cloudSecurityPosture" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudSecurityPosture plugin -date: 2024-05-16 +date: 2024-05-20 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 08883611c4695..c8c436075e006 100644 --- a/api_docs/console.mdx +++ b/api_docs/console.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/console title: "console" image: https://source.unsplash.com/400x175/?github description: API docs for the console plugin -date: 2024-05-16 +date: 2024-05-20 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 15204bfbaaef6..3d97d8d324d82 100644 --- a/api_docs/content_management.mdx +++ b/api_docs/content_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/contentManagement title: "contentManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the contentManagement plugin -date: 2024-05-16 +date: 2024-05-20 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 ccd3afd2d7b93..4bb2d4f5b695e 100644 --- a/api_docs/controls.mdx +++ b/api_docs/controls.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/controls title: "controls" image: https://source.unsplash.com/400x175/?github description: API docs for the controls plugin -date: 2024-05-16 +date: 2024-05-20 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 fae2434757ca3..d20c0d817bad2 100644 --- a/api_docs/custom_integrations.mdx +++ b/api_docs/custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/customIntegrations title: "customIntegrations" image: https://source.unsplash.com/400x175/?github description: API docs for the customIntegrations plugin -date: 2024-05-16 +date: 2024-05-20 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 f583cf499cd61..ebc2ca136d543 100644 --- a/api_docs/dashboard.mdx +++ b/api_docs/dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboard title: "dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboard plugin -date: 2024-05-16 +date: 2024-05-20 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 41def7fc2c39a..ed8a799c2e890 100644 --- a/api_docs/dashboard_enhanced.mdx +++ b/api_docs/dashboard_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboardEnhanced title: "dashboardEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboardEnhanced plugin -date: 2024-05-16 +date: 2024-05-20 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 3460a1db5878c..1a5d1f793c624 100644 --- a/api_docs/data.mdx +++ b/api_docs/data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data title: "data" image: https://source.unsplash.com/400x175/?github description: API docs for the data plugin -date: 2024-05-16 +date: 2024-05-20 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 814f2e6e33409..72ec9098d970b 100644 --- a/api_docs/data_query.mdx +++ b/api_docs/data_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-query title: "data.query" image: https://source.unsplash.com/400x175/?github description: API docs for the data.query plugin -date: 2024-05-16 +date: 2024-05-20 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 7816b8061baa8..f9e1313b825b5 100644 --- a/api_docs/data_search.mdx +++ b/api_docs/data_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-search title: "data.search" image: https://source.unsplash.com/400x175/?github description: API docs for the data.search plugin -date: 2024-05-16 +date: 2024-05-20 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 357d0c5ec4331..e14fbb84c8c3e 100644 --- a/api_docs/data_view_editor.mdx +++ b/api_docs/data_view_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewEditor title: "dataViewEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewEditor plugin -date: 2024-05-16 +date: 2024-05-20 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 af308874cf2f5..47f679b648687 100644 --- a/api_docs/data_view_field_editor.mdx +++ b/api_docs/data_view_field_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewFieldEditor title: "dataViewFieldEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewFieldEditor plugin -date: 2024-05-16 +date: 2024-05-20 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 9297b18e77dba..dc854eb2bc307 100644 --- a/api_docs/data_view_management.mdx +++ b/api_docs/data_view_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewManagement title: "dataViewManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewManagement plugin -date: 2024-05-16 +date: 2024-05-20 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 49f3fa11de8e3..be2d13723e34b 100644 --- a/api_docs/data_views.mdx +++ b/api_docs/data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViews title: "dataViews" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViews plugin -date: 2024-05-16 +date: 2024-05-20 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 8894797458689..0ce9929024c75 100644 --- a/api_docs/data_visualizer.mdx +++ b/api_docs/data_visualizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataVisualizer title: "dataVisualizer" image: https://source.unsplash.com/400x175/?github description: API docs for the dataVisualizer plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer'] --- import dataVisualizerObj from './data_visualizer.devdocs.json'; diff --git a/api_docs/dataset_quality.mdx b/api_docs/dataset_quality.mdx index 611341551695d..5cc8ac5c6b11b 100644 --- a/api_docs/dataset_quality.mdx +++ b/api_docs/dataset_quality.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/datasetQuality title: "datasetQuality" image: https://source.unsplash.com/400x175/?github description: API docs for the datasetQuality plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'datasetQuality'] --- import datasetQualityObj from './dataset_quality.devdocs.json'; diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index a75ee0249320a..7fdfb650df82d 100644 --- a/api_docs/deprecations_by_api.mdx +++ b/api_docs/deprecations_by_api.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByApi slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-api title: Deprecated API usage by API description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 6d48bf1b4664b..18390038fa500 100644 --- a/api_docs/deprecations_by_plugin.mdx +++ b/api_docs/deprecations_by_plugin.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByPlugin slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-plugin title: Deprecated API usage by plugin description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index 2ad775a309d1e..919da7d93a174 100644 --- a/api_docs/deprecations_by_team.mdx +++ b/api_docs/deprecations_by_team.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsDueByTeam slug: /kibana-dev-docs/api-meta/deprecations-due-by-team title: Deprecated APIs due to be removed, by team description: Lists the teams that are referencing deprecated APIs with a remove by date. -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index b1e08d753d260..fe61ae6d29a50 100644 --- a/api_docs/dev_tools.mdx +++ b/api_docs/dev_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/devTools title: "devTools" image: https://source.unsplash.com/400x175/?github description: API docs for the devTools plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'devTools'] --- import devToolsObj from './dev_tools.devdocs.json'; diff --git a/api_docs/discover.devdocs.json b/api_docs/discover.devdocs.json index 7952724d9e5d0..1e2946e6fa780 100644 --- a/api_docs/discover.devdocs.json +++ b/api_docs/discover.devdocs.json @@ -117,10 +117,10 @@ }, { "parentPluginId": "discover", - "id": "def-public.DataDocumentsMsg.textBasedQueryColumns", + "id": "def-public.DataDocumentsMsg.esqlQueryColumns", "type": "Array", "tags": [], - "label": "textBasedQueryColumns", + "label": "esqlQueryColumns", "description": [], "signature": [ { @@ -138,10 +138,10 @@ }, { "parentPluginId": "discover", - "id": "def-public.DataDocumentsMsg.textBasedHeaderWarning", + "id": "def-public.DataDocumentsMsg.esqlHeaderWarning", "type": "string", "tags": [], - "label": "textBasedHeaderWarning", + "label": "esqlHeaderWarning", "description": [], "signature": [ "string | undefined" diff --git a/api_docs/discover.mdx b/api_docs/discover.mdx index a51c6e121bc7c..14de89d8f6e4b 100644 --- a/api_docs/discover.mdx +++ b/api_docs/discover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discover title: "discover" image: https://source.unsplash.com/400x175/?github description: API docs for the discover plugin -date: 2024-05-16 +date: 2024-05-20 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 941e41441a33c..653c09fe0385f 100644 --- a/api_docs/discover_enhanced.mdx +++ b/api_docs/discover_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discoverEnhanced title: "discoverEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the discoverEnhanced plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] --- import discoverEnhancedObj from './discover_enhanced.devdocs.json'; diff --git a/api_docs/discover_shared.mdx b/api_docs/discover_shared.mdx index baf360a03c07e..97c75e0d7949e 100644 --- a/api_docs/discover_shared.mdx +++ b/api_docs/discover_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discoverShared title: "discoverShared" image: https://source.unsplash.com/400x175/?github description: API docs for the discoverShared plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverShared'] --- import discoverSharedObj from './discover_shared.devdocs.json'; diff --git a/api_docs/ecs_data_quality_dashboard.mdx b/api_docs/ecs_data_quality_dashboard.mdx index 53b45384bf92f..4c6d8f24223ab 100644 --- a/api_docs/ecs_data_quality_dashboard.mdx +++ b/api_docs/ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ecsDataQualityDashboard title: "ecsDataQualityDashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the ecsDataQualityDashboard plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ecsDataQualityDashboard'] --- import ecsDataQualityDashboardObj from './ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/elastic_assistant.mdx b/api_docs/elastic_assistant.mdx index c44e9b4170178..c1d9e7f1f3dab 100644 --- a/api_docs/elastic_assistant.mdx +++ b/api_docs/elastic_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/elasticAssistant title: "elasticAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the elasticAssistant plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'elasticAssistant'] --- import elasticAssistantObj from './elastic_assistant.devdocs.json'; diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index 66e0d04cfa183..757f36c217e31 100644 --- a/api_docs/embeddable.mdx +++ b/api_docs/embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddable title: "embeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddable plugin -date: 2024-05-16 +date: 2024-05-20 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 04015ef4a968d..3bd9e45fbb55f 100644 --- a/api_docs/embeddable_enhanced.mdx +++ b/api_docs/embeddable_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddableEnhanced title: "embeddableEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddableEnhanced plugin -date: 2024-05-16 +date: 2024-05-20 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 5f50b6d2a374f..0f027d6476aab 100644 --- a/api_docs/encrypted_saved_objects.mdx +++ b/api_docs/encrypted_saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/encryptedSavedObjects title: "encryptedSavedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the encryptedSavedObjects plugin -date: 2024-05-16 +date: 2024-05-20 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 a56395e3551fd..df612f3ba58c0 100644 --- a/api_docs/enterprise_search.mdx +++ b/api_docs/enterprise_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/enterpriseSearch title: "enterpriseSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the enterpriseSearch plugin -date: 2024-05-16 +date: 2024-05-20 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 350d16f962335..bfbce14d2f28a 100644 --- a/api_docs/es_ui_shared.mdx +++ b/api_docs/es_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esUiShared title: "esUiShared" image: https://source.unsplash.com/400x175/?github description: API docs for the esUiShared plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esUiShared'] --- import esUiSharedObj from './es_ui_shared.devdocs.json'; diff --git a/api_docs/event_annotation.mdx b/api_docs/event_annotation.mdx index 1dc957e6d2d6a..a143a6710e4fa 100644 --- a/api_docs/event_annotation.mdx +++ b/api_docs/event_annotation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotation title: "eventAnnotation" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotation plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation'] --- import eventAnnotationObj from './event_annotation.devdocs.json'; diff --git a/api_docs/event_annotation_listing.mdx b/api_docs/event_annotation_listing.mdx index b60e428341af6..6305e112cb763 100644 --- a/api_docs/event_annotation_listing.mdx +++ b/api_docs/event_annotation_listing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotationListing title: "eventAnnotationListing" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotationListing plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotationListing'] --- import eventAnnotationListingObj from './event_annotation_listing.devdocs.json'; diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx index b63dba1e6cf2e..67bf2d55c88ca 100644 --- a/api_docs/event_log.mdx +++ b/api_docs/event_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventLog title: "eventLog" image: https://source.unsplash.com/400x175/?github description: API docs for the eventLog plugin -date: 2024-05-16 +date: 2024-05-20 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 220f4967a081d..ea5ad4f14611c 100644 --- a/api_docs/exploratory_view.mdx +++ b/api_docs/exploratory_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/exploratoryView title: "exploratoryView" image: https://source.unsplash.com/400x175/?github description: API docs for the exploratoryView plugin -date: 2024-05-16 +date: 2024-05-20 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 73045b4468e1e..13ddadc0355f1 100644 --- a/api_docs/expression_error.mdx +++ b/api_docs/expression_error.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionError title: "expressionError" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionError plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionError'] --- import expressionErrorObj from './expression_error.devdocs.json'; diff --git a/api_docs/expression_gauge.mdx b/api_docs/expression_gauge.mdx index 61a1d71f765d6..7b7e8b16fe3d9 100644 --- a/api_docs/expression_gauge.mdx +++ b/api_docs/expression_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionGauge title: "expressionGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionGauge plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionGauge'] --- import expressionGaugeObj from './expression_gauge.devdocs.json'; diff --git a/api_docs/expression_heatmap.mdx b/api_docs/expression_heatmap.mdx index 523a114e1e2cb..36a025e989bd6 100644 --- a/api_docs/expression_heatmap.mdx +++ b/api_docs/expression_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionHeatmap title: "expressionHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionHeatmap plugin -date: 2024-05-16 +date: 2024-05-20 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 6afb86526b829..2ed037401a5ee 100644 --- a/api_docs/expression_image.mdx +++ b/api_docs/expression_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionImage title: "expressionImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionImage plugin -date: 2024-05-16 +date: 2024-05-20 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 6ffb985dd5a99..0a2f7f52ae824 100644 --- a/api_docs/expression_legacy_metric_vis.mdx +++ b/api_docs/expression_legacy_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionLegacyMetricVis title: "expressionLegacyMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionLegacyMetricVis plugin -date: 2024-05-16 +date: 2024-05-20 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 980a58c757f0b..de0d33b3fe139 100644 --- a/api_docs/expression_metric.mdx +++ b/api_docs/expression_metric.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetric title: "expressionMetric" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetric plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetric'] --- import expressionMetricObj from './expression_metric.devdocs.json'; diff --git a/api_docs/expression_metric_vis.mdx b/api_docs/expression_metric_vis.mdx index 829a73965f30f..81552f2a71390 100644 --- a/api_docs/expression_metric_vis.mdx +++ b/api_docs/expression_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetricVis title: "expressionMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetricVis plugin -date: 2024-05-16 +date: 2024-05-20 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 73fd6d03e5ecb..0d361a1ef6ee5 100644 --- a/api_docs/expression_partition_vis.mdx +++ b/api_docs/expression_partition_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionPartitionVis title: "expressionPartitionVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionPartitionVis plugin -date: 2024-05-16 +date: 2024-05-20 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 94526867e6148..3221b1bc91391 100644 --- a/api_docs/expression_repeat_image.mdx +++ b/api_docs/expression_repeat_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRepeatImage title: "expressionRepeatImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRepeatImage plugin -date: 2024-05-16 +date: 2024-05-20 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 fa5f044da787a..c41d008ceab6d 100644 --- a/api_docs/expression_reveal_image.mdx +++ b/api_docs/expression_reveal_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRevealImage title: "expressionRevealImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRevealImage plugin -date: 2024-05-16 +date: 2024-05-20 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 d9e44a4da3710..857bb8256b2ec 100644 --- a/api_docs/expression_shape.mdx +++ b/api_docs/expression_shape.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionShape title: "expressionShape" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionShape plugin -date: 2024-05-16 +date: 2024-05-20 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 5939e46f6936f..36feeaee47c02 100644 --- a/api_docs/expression_tagcloud.mdx +++ b/api_docs/expression_tagcloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionTagcloud title: "expressionTagcloud" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionTagcloud plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionTagcloud'] --- import expressionTagcloudObj from './expression_tagcloud.devdocs.json'; diff --git a/api_docs/expression_x_y.mdx b/api_docs/expression_x_y.mdx index f5b7aa5cd5845..9a90063d9957a 100644 --- a/api_docs/expression_x_y.mdx +++ b/api_docs/expression_x_y.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionXY title: "expressionXY" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionXY plugin -date: 2024-05-16 +date: 2024-05-20 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 16e98c2ae7717..8e952fd651860 100644 --- a/api_docs/expressions.mdx +++ b/api_docs/expressions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressions title: "expressions" image: https://source.unsplash.com/400x175/?github description: API docs for the expressions plugin -date: 2024-05-16 +date: 2024-05-20 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 6923aa2754f6f..e06c1d1c23064 100644 --- a/api_docs/features.mdx +++ b/api_docs/features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/features title: "features" image: https://source.unsplash.com/400x175/?github description: API docs for the features plugin -date: 2024-05-16 +date: 2024-05-20 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 4bf756dd1d889..a693e34dcd748 100644 --- a/api_docs/field_formats.mdx +++ b/api_docs/field_formats.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fieldFormats title: "fieldFormats" image: https://source.unsplash.com/400x175/?github description: API docs for the fieldFormats plugin -date: 2024-05-16 +date: 2024-05-20 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 f5de1a1f85ada..870212a3293ee 100644 --- a/api_docs/file_upload.mdx +++ b/api_docs/file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fileUpload title: "fileUpload" image: https://source.unsplash.com/400x175/?github description: API docs for the fileUpload plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fileUpload'] --- import fileUploadObj from './file_upload.devdocs.json'; diff --git a/api_docs/files.mdx b/api_docs/files.mdx index 182acfe1b6390..7bb838c05c90f 100644 --- a/api_docs/files.mdx +++ b/api_docs/files.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/files title: "files" image: https://source.unsplash.com/400x175/?github description: API docs for the files plugin -date: 2024-05-16 +date: 2024-05-20 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 a6e62076dc23d..ac9ae8788cf90 100644 --- a/api_docs/files_management.mdx +++ b/api_docs/files_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/filesManagement title: "filesManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the filesManagement plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'filesManagement'] --- import filesManagementObj from './files_management.devdocs.json'; diff --git a/api_docs/fleet.devdocs.json b/api_docs/fleet.devdocs.json index 06263703c93a7..9767c0c9487f8 100644 --- a/api_docs/fleet.devdocs.json +++ b/api_docs/fleet.devdocs.json @@ -24997,7 +24997,7 @@ "label": "[RegistryVarsEntryKeys.type]", "description": [], "signature": [ - "\"string\" | \"text\" | \"integer\" | \"select\" | \"textarea\" | \"bool\" | \"password\" | \"yaml\"" + "\"string\" | \"text\" | \"integer\" | \"password\" | \"select\" | \"textarea\" | \"bool\" | \"yaml\"" ], "path": "x-pack/plugins/fleet/common/types/models/epm.ts", "deprecated": false, diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index b2f676f7a579b..75ac3909b9408 100644 --- a/api_docs/fleet.mdx +++ b/api_docs/fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fleet title: "fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the fleet plugin -date: 2024-05-16 +date: 2024-05-20 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 671520298f10b..ee0269cb007a6 100644 --- a/api_docs/global_search.mdx +++ b/api_docs/global_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/globalSearch title: "globalSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the globalSearch plugin -date: 2024-05-16 +date: 2024-05-20 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 0be56866276c1..3541f8296b9b6 100644 --- a/api_docs/guided_onboarding.mdx +++ b/api_docs/guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/guidedOnboarding title: "guidedOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the guidedOnboarding plugin -date: 2024-05-16 +date: 2024-05-20 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 c4c76a646a278..8f14b511df193 100644 --- a/api_docs/home.mdx +++ b/api_docs/home.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/home title: "home" image: https://source.unsplash.com/400x175/?github description: API docs for the home plugin -date: 2024-05-16 +date: 2024-05-20 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 226c0c9d79faa..5f558f384c873 100644 --- a/api_docs/image_embeddable.mdx +++ b/api_docs/image_embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/imageEmbeddable title: "imageEmbeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the imageEmbeddable plugin -date: 2024-05-16 +date: 2024-05-20 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 ac1ed0d7ef227..3f71772224b12 100644 --- a/api_docs/index_lifecycle_management.mdx +++ b/api_docs/index_lifecycle_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexLifecycleManagement title: "indexLifecycleManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexLifecycleManagement plugin -date: 2024-05-16 +date: 2024-05-20 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 be5ab07e36a86..ac72aa6d904f7 100644 --- a/api_docs/index_management.mdx +++ b/api_docs/index_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexManagement title: "indexManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexManagement plugin -date: 2024-05-16 +date: 2024-05-20 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 5deea1da49242..d85f5f4552cb1 100644 --- a/api_docs/infra.mdx +++ b/api_docs/infra.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/infra title: "infra" image: https://source.unsplash.com/400x175/?github description: API docs for the infra plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra'] --- import infraObj from './infra.devdocs.json'; diff --git a/api_docs/ingest_pipelines.mdx b/api_docs/ingest_pipelines.mdx index 0c0d2ca29758b..a72b9a6905658 100644 --- a/api_docs/ingest_pipelines.mdx +++ b/api_docs/ingest_pipelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ingestPipelines title: "ingestPipelines" image: https://source.unsplash.com/400x175/?github description: API docs for the ingestPipelines plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ingestPipelines'] --- import ingestPipelinesObj from './ingest_pipelines.devdocs.json'; diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx index 91f4cc6bbec82..ce43a012265d9 100644 --- a/api_docs/inspector.mdx +++ b/api_docs/inspector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/inspector title: "inspector" image: https://source.unsplash.com/400x175/?github description: API docs for the inspector plugin -date: 2024-05-16 +date: 2024-05-20 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 e281d483c7394..5d01194336435 100644 --- a/api_docs/interactive_setup.mdx +++ b/api_docs/interactive_setup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/interactiveSetup title: "interactiveSetup" image: https://source.unsplash.com/400x175/?github description: API docs for the interactiveSetup plugin -date: 2024-05-16 +date: 2024-05-20 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 484093a43dd35..75116cba2206b 100644 --- a/api_docs/kbn_ace.mdx +++ b/api_docs/kbn_ace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ace title: "@kbn/ace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ace plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ace'] --- import kbnAceObj from './kbn_ace.devdocs.json'; diff --git a/api_docs/kbn_actions_types.mdx b/api_docs/kbn_actions_types.mdx index 3dadb854124c5..2218cc06aec6f 100644 --- a/api_docs/kbn_actions_types.mdx +++ b/api_docs/kbn_actions_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-actions-types title: "@kbn/actions-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/actions-types plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/actions-types'] --- import kbnActionsTypesObj from './kbn_actions_types.devdocs.json'; diff --git a/api_docs/kbn_aiops_components.mdx b/api_docs/kbn_aiops_components.mdx index b5e1c5457fa94..37b1ce851614a 100644 --- a/api_docs/kbn_aiops_components.mdx +++ b/api_docs/kbn_aiops_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-components title: "@kbn/aiops-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-components plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] --- import kbnAiopsComponentsObj from './kbn_aiops_components.devdocs.json'; diff --git a/api_docs/kbn_aiops_log_pattern_analysis.mdx b/api_docs/kbn_aiops_log_pattern_analysis.mdx index 1f94eddb6a674..6d2d3f466d0b7 100644 --- a/api_docs/kbn_aiops_log_pattern_analysis.mdx +++ b/api_docs/kbn_aiops_log_pattern_analysis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-log-pattern-analysis title: "@kbn/aiops-log-pattern-analysis" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-log-pattern-analysis plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-log-pattern-analysis'] --- import kbnAiopsLogPatternAnalysisObj from './kbn_aiops_log_pattern_analysis.devdocs.json'; diff --git a/api_docs/kbn_aiops_log_rate_analysis.mdx b/api_docs/kbn_aiops_log_rate_analysis.mdx index 0a2b8d0ffda8b..a244910fe2af8 100644 --- a/api_docs/kbn_aiops_log_rate_analysis.mdx +++ b/api_docs/kbn_aiops_log_rate_analysis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-log-rate-analysis title: "@kbn/aiops-log-rate-analysis" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-log-rate-analysis plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-log-rate-analysis'] --- import kbnAiopsLogRateAnalysisObj from './kbn_aiops_log_rate_analysis.devdocs.json'; diff --git a/api_docs/kbn_alerting_api_integration_helpers.mdx b/api_docs/kbn_alerting_api_integration_helpers.mdx index c80416028ed38..bd3e35c910950 100644 --- a/api_docs/kbn_alerting_api_integration_helpers.mdx +++ b/api_docs/kbn_alerting_api_integration_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-api-integration-helpers title: "@kbn/alerting-api-integration-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-api-integration-helpers plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-api-integration-helpers'] --- import kbnAlertingApiIntegrationHelpersObj from './kbn_alerting_api_integration_helpers.devdocs.json'; diff --git a/api_docs/kbn_alerting_state_types.mdx b/api_docs/kbn_alerting_state_types.mdx index 1f9abea4f1533..f8a768273a666 100644 --- a/api_docs/kbn_alerting_state_types.mdx +++ b/api_docs/kbn_alerting_state_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-state-types title: "@kbn/alerting-state-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-state-types plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-state-types'] --- import kbnAlertingStateTypesObj from './kbn_alerting_state_types.devdocs.json'; diff --git a/api_docs/kbn_alerting_types.mdx b/api_docs/kbn_alerting_types.mdx index 5451c5ddcd328..f69d8259e7554 100644 --- a/api_docs/kbn_alerting_types.mdx +++ b/api_docs/kbn_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-types title: "@kbn/alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-types plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-types'] --- import kbnAlertingTypesObj from './kbn_alerting_types.devdocs.json'; diff --git a/api_docs/kbn_alerts_as_data_utils.mdx b/api_docs/kbn_alerts_as_data_utils.mdx index 22579c3f822d2..a40a003ea3ad4 100644 --- a/api_docs/kbn_alerts_as_data_utils.mdx +++ b/api_docs/kbn_alerts_as_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-as-data-utils title: "@kbn/alerts-as-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-as-data-utils plugin -date: 2024-05-16 +date: 2024-05-20 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 41281828da834..0ceaddc5d97c6 100644 --- a/api_docs/kbn_alerts_ui_shared.mdx +++ b/api_docs/kbn_alerts_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-ui-shared title: "@kbn/alerts-ui-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-ui-shared plugin -date: 2024-05-16 +date: 2024-05-20 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 167d70babc516..e6d44f5fc12af 100644 --- a/api_docs/kbn_analytics.mdx +++ b/api_docs/kbn_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics title: "@kbn/analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_client.devdocs.json b/api_docs/kbn_analytics_client.devdocs.json index 382387f6ace2f..02f60c3586d6b 100644 --- a/api_docs/kbn_analytics_client.devdocs.json +++ b/api_docs/kbn_analytics_client.devdocs.json @@ -710,6 +710,42 @@ "plugin": "security", "path": "x-pack/plugins/security/server/analytics/analytics_service.ts" }, + { + "plugin": "@kbn/cloud", + "path": "packages/cloud/connection_details/kibana/kibana_connection_details_provider.tsx" + }, + { + "plugin": "@kbn/cloud", + "path": "packages/cloud/connection_details/kibana/kibana_connection_details_provider.tsx" + }, + { + "plugin": "@kbn/cloud", + "path": "packages/cloud/connection_details/kibana/kibana_connection_details_provider.tsx" + }, + { + "plugin": "@kbn/cloud", + "path": "packages/cloud/connection_details/kibana/kibana_connection_details_provider.tsx" + }, + { + "plugin": "@kbn/cloud", + "path": "packages/cloud/connection_details/kibana/kibana_connection_details_provider.tsx" + }, + { + "plugin": "@kbn/cloud", + "path": "packages/cloud/connection_details/kibana/kibana_connection_details_provider.tsx" + }, + { + "plugin": "@kbn/cloud", + "path": "packages/cloud/connection_details/kibana/kibana_connection_details_provider.tsx" + }, + { + "plugin": "@kbn/cloud", + "path": "packages/cloud/connection_details/kibana/kibana_connection_details_provider.tsx" + }, + { + "plugin": "@kbn/cloud", + "path": "packages/cloud/connection_details/kibana/kibana_connection_details_provider.tsx" + }, { "plugin": "dashboard", "path": "src/plugins/dashboard/public/services/analytics/types.ts" @@ -742,6 +778,10 @@ "plugin": "fleet", "path": "x-pack/plugins/fleet/server/services/telemetry/fleet_usage_sender.ts" }, + { + "plugin": "fleet", + "path": "x-pack/plugins/fleet/server/services/telemetry/fleet_usage_sender.ts" + }, { "plugin": "elasticAssistant", "path": "x-pack/plugins/elastic_assistant/server/lib/langchain/elasticsearch_store/elasticsearch_store.ts" diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx index 8af754a8a8134..11475be46c8e8 100644 --- a/api_docs/kbn_analytics_client.mdx +++ b/api_docs/kbn_analytics_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-client title: "@kbn/analytics-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-client plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-client'] --- import kbnAnalyticsClientObj from './kbn_analytics_client.devdocs.json'; diff --git a/api_docs/kbn_analytics_collection_utils.mdx b/api_docs/kbn_analytics_collection_utils.mdx index 4bd2e6f8f9de8..a63f4e6cc0ff2 100644 --- a/api_docs/kbn_analytics_collection_utils.mdx +++ b/api_docs/kbn_analytics_collection_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-collection-utils title: "@kbn/analytics-collection-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-collection-utils plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-collection-utils'] --- import kbnAnalyticsCollectionUtilsObj from './kbn_analytics_collection_utils.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx index 5e94d8b84521b..7d57f62f66383 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-browser title: "@kbn/analytics-shippers-elastic-v3-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-browser plugin -date: 2024-05-16 +date: 2024-05-20 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 b7f15489d259b..2454fb1be5b45 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-common title: "@kbn/analytics-shippers-elastic-v3-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-common plugin -date: 2024-05-16 +date: 2024-05-20 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 778d494d098d6..3a6988d2f1083 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-server title: "@kbn/analytics-shippers-elastic-v3-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-server plugin -date: 2024-05-16 +date: 2024-05-20 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 e74508d4deb8d..2194b53b01c67 100644 --- a/api_docs/kbn_analytics_shippers_fullstory.mdx +++ b/api_docs/kbn_analytics_shippers_fullstory.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-fullstory title: "@kbn/analytics-shippers-fullstory" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-fullstory plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-fullstory'] --- import kbnAnalyticsShippersFullstoryObj from './kbn_analytics_shippers_fullstory.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index 88eefe81fb0e1..d24056e55af3a 100644 --- a/api_docs/kbn_apm_config_loader.mdx +++ b/api_docs/kbn_apm_config_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-config-loader title: "@kbn/apm-config-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-config-loader plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-config-loader'] --- import kbnApmConfigLoaderObj from './kbn_apm_config_loader.devdocs.json'; diff --git a/api_docs/kbn_apm_data_view.mdx b/api_docs/kbn_apm_data_view.mdx index 1d825bbf4cb1c..4ebcfcea51128 100644 --- a/api_docs/kbn_apm_data_view.mdx +++ b/api_docs/kbn_apm_data_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-data-view title: "@kbn/apm-data-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-data-view plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-data-view'] --- import kbnApmDataViewObj from './kbn_apm_data_view.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace.mdx b/api_docs/kbn_apm_synthtrace.mdx index 0d07b1a2e3948..5a2b271647f7a 100644 --- a/api_docs/kbn_apm_synthtrace.mdx +++ b/api_docs/kbn_apm_synthtrace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace title: "@kbn/apm-synthtrace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace plugin -date: 2024-05-16 +date: 2024-05-20 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.devdocs.json b/api_docs/kbn_apm_synthtrace_client.devdocs.json index 8b1bb3e1fbd0f..935496013d4fc 100644 --- a/api_docs/kbn_apm_synthtrace_client.devdocs.json +++ b/api_docs/kbn_apm_synthtrace_client.devdocs.json @@ -2632,7 +2632,9 @@ " | ", "PodMetricsDocument", " | ", - "ContainerMetricsDocument" + "DockerContainerMetricsDocument", + " | ", + "K8sContainerMetricsDocument" ], "path": "packages/kbn-apm-synthtrace-client/src/lib/infra/index.ts", "deprecated": false, @@ -2926,14 +2928,43 @@ }, { "parentPluginId": "@kbn/apm-synthtrace-client", - "id": "def-common.infra.container", + "id": "def-common.infra.dockerContainer", + "type": "Function", + "tags": [], + "label": "dockerContainer", + "description": [], + "signature": [ + "(id: string) => ", + "DockerContainer" + ], + "path": "packages/kbn-apm-synthtrace-client/src/lib/infra/index.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/apm-synthtrace-client", + "id": "def-common.infra.dockerContainer.$1", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "path": "packages/kbn-apm-synthtrace-client/src/lib/infra/docker_container.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/apm-synthtrace-client", + "id": "def-common.infra.k8sContainer", "type": "Function", "tags": [], - "label": "container", + "label": "k8sContainer", "description": [], "signature": [ "(id: string, uid: string, nodeName: string) => ", - "Container" + "K8sContainer" ], "path": "packages/kbn-apm-synthtrace-client/src/lib/infra/index.ts", "deprecated": false, @@ -2942,34 +2973,34 @@ "children": [ { "parentPluginId": "@kbn/apm-synthtrace-client", - "id": "def-common.infra.container.$1", + "id": "def-common.infra.k8sContainer.$1", "type": "string", "tags": [], "label": "id", "description": [], - "path": "packages/kbn-apm-synthtrace-client/src/lib/infra/container.ts", + "path": "packages/kbn-apm-synthtrace-client/src/lib/infra/k8s_container.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/apm-synthtrace-client", - "id": "def-common.infra.container.$2", + "id": "def-common.infra.k8sContainer.$2", "type": "string", "tags": [], "label": "uid", "description": [], - "path": "packages/kbn-apm-synthtrace-client/src/lib/infra/container.ts", + "path": "packages/kbn-apm-synthtrace-client/src/lib/infra/k8s_container.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/apm-synthtrace-client", - "id": "def-common.infra.container.$3", + "id": "def-common.infra.k8sContainer.$3", "type": "string", "tags": [], "label": "nodeName", "description": [], - "path": "packages/kbn-apm-synthtrace-client/src/lib/infra/container.ts", + "path": "packages/kbn-apm-synthtrace-client/src/lib/infra/k8s_container.ts", "deprecated": false, "trackAdoption": false } diff --git a/api_docs/kbn_apm_synthtrace_client.mdx b/api_docs/kbn_apm_synthtrace_client.mdx index f7b8e921503ca..33a7e8454d362 100644 --- a/api_docs/kbn_apm_synthtrace_client.mdx +++ b/api_docs/kbn_apm_synthtrace_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace-client title: "@kbn/apm-synthtrace-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace-client plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace-client'] --- import kbnApmSynthtraceClientObj from './kbn_apm_synthtrace_client.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/te | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 191 | 0 | 191 | 28 | +| 193 | 0 | 193 | 30 | ## Common diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index 26a1b65232fb1..b8771eaff3bb9 100644 --- a/api_docs/kbn_apm_utils.mdx +++ b/api_docs/kbn_apm_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-utils title: "@kbn/apm-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-utils plugin -date: 2024-05-16 +date: 2024-05-20 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 313225279f097..cd2250bbfe5ac 100644 --- a/api_docs/kbn_axe_config.mdx +++ b/api_docs/kbn_axe_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-axe-config title: "@kbn/axe-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/axe-config plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_bfetch_error.mdx b/api_docs/kbn_bfetch_error.mdx index dd189e1f404ab..18493c8dc8718 100644 --- a/api_docs/kbn_bfetch_error.mdx +++ b/api_docs/kbn_bfetch_error.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-bfetch-error title: "@kbn/bfetch-error" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/bfetch-error plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/bfetch-error'] --- import kbnBfetchErrorObj from './kbn_bfetch_error.devdocs.json'; diff --git a/api_docs/kbn_calculate_auto.mdx b/api_docs/kbn_calculate_auto.mdx index 4f34e2d02b964..d2b31ce98859c 100644 --- a/api_docs/kbn_calculate_auto.mdx +++ b/api_docs/kbn_calculate_auto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-calculate-auto title: "@kbn/calculate-auto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/calculate-auto plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/calculate-auto'] --- import kbnCalculateAutoObj from './kbn_calculate_auto.devdocs.json'; diff --git a/api_docs/kbn_calculate_width_from_char_count.mdx b/api_docs/kbn_calculate_width_from_char_count.mdx index 78998c799a362..0eee9d95b4a8f 100644 --- a/api_docs/kbn_calculate_width_from_char_count.mdx +++ b/api_docs/kbn_calculate_width_from_char_count.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-calculate-width-from-char-count title: "@kbn/calculate-width-from-char-count" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/calculate-width-from-char-count plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/calculate-width-from-char-count'] --- import kbnCalculateWidthFromCharCountObj from './kbn_calculate_width_from_char_count.devdocs.json'; diff --git a/api_docs/kbn_cases_components.mdx b/api_docs/kbn_cases_components.mdx index 90d5c01339d3c..29518f89e5848 100644 --- a/api_docs/kbn_cases_components.mdx +++ b/api_docs/kbn_cases_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cases-components title: "@kbn/cases-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cases-components plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cases-components'] --- import kbnCasesComponentsObj from './kbn_cases_components.devdocs.json'; diff --git a/api_docs/kbn_cell_actions.mdx b/api_docs/kbn_cell_actions.mdx index 27c57d4fcc10e..6188e8d4e4310 100644 --- a/api_docs/kbn_cell_actions.mdx +++ b/api_docs/kbn_cell_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cell-actions title: "@kbn/cell-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cell-actions plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cell-actions'] --- import kbnCellActionsObj from './kbn_cell_actions.devdocs.json'; diff --git a/api_docs/kbn_chart_expressions_common.mdx b/api_docs/kbn_chart_expressions_common.mdx index 50714ad160bd6..d1060783d23e5 100644 --- a/api_docs/kbn_chart_expressions_common.mdx +++ b/api_docs/kbn_chart_expressions_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-expressions-common title: "@kbn/chart-expressions-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-expressions-common plugin -date: 2024-05-16 +date: 2024-05-20 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 c51b6f749cd2c..534410c9302da 100644 --- a/api_docs/kbn_chart_icons.mdx +++ b/api_docs/kbn_chart_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-icons title: "@kbn/chart-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-icons plugin -date: 2024-05-16 +date: 2024-05-20 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 86d11ebad8aad..b0e473d635928 100644 --- a/api_docs/kbn_ci_stats_core.mdx +++ b/api_docs/kbn_ci_stats_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-core title: "@kbn/ci-stats-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-core plugin -date: 2024-05-16 +date: 2024-05-20 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 6dc2a213ae99b..9f8ec810a0beb 100644 --- a/api_docs/kbn_ci_stats_performance_metrics.mdx +++ b/api_docs/kbn_ci_stats_performance_metrics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-performance-metrics title: "@kbn/ci-stats-performance-metrics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-performance-metrics plugin -date: 2024-05-16 +date: 2024-05-20 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 b57dbea34a842..ce73f8f8cca07 100644 --- a/api_docs/kbn_ci_stats_reporter.mdx +++ b/api_docs/kbn_ci_stats_reporter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-reporter title: "@kbn/ci-stats-reporter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-reporter plugin -date: 2024-05-16 +date: 2024-05-20 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 e582564a36996..69067a211490d 100644 --- a/api_docs/kbn_cli_dev_mode.mdx +++ b/api_docs/kbn_cli_dev_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cli-dev-mode title: "@kbn/cli-dev-mode" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cli-dev-mode plugin -date: 2024-05-16 +date: 2024-05-20 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 4297a71a9ec2e..6ab116495d9bb 100644 --- a/api_docs/kbn_code_editor.mdx +++ b/api_docs/kbn_code_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor title: "@kbn/code-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor'] --- import kbnCodeEditorObj from './kbn_code_editor.devdocs.json'; diff --git a/api_docs/kbn_code_editor_mock.mdx b/api_docs/kbn_code_editor_mock.mdx index 44fcdb779f497..e5a09418a4cf7 100644 --- a/api_docs/kbn_code_editor_mock.mdx +++ b/api_docs/kbn_code_editor_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor-mock title: "@kbn/code-editor-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor-mock plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor-mock'] --- import kbnCodeEditorMockObj from './kbn_code_editor_mock.devdocs.json'; diff --git a/api_docs/kbn_code_owners.mdx b/api_docs/kbn_code_owners.mdx index ac9e07ebee19e..86fa8a1239df4 100644 --- a/api_docs/kbn_code_owners.mdx +++ b/api_docs/kbn_code_owners.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-owners title: "@kbn/code-owners" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-owners plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-owners'] --- import kbnCodeOwnersObj from './kbn_code_owners.devdocs.json'; diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index 6148173599693..cd38f5bf1a8d2 100644 --- a/api_docs/kbn_coloring.mdx +++ b/api_docs/kbn_coloring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-coloring title: "@kbn/coloring" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/coloring plugin -date: 2024-05-16 +date: 2024-05-20 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 dc387b234aea5..3f71b281bebfe 100644 --- a/api_docs/kbn_config.mdx +++ b/api_docs/kbn_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config title: "@kbn/config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config plugin -date: 2024-05-16 +date: 2024-05-20 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 fe91e549c307a..b860f3d4dbb49 100644 --- a/api_docs/kbn_config_mocks.mdx +++ b/api_docs/kbn_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-mocks title: "@kbn/config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 3dce17613c957..99427ae691302 100644 --- a/api_docs/kbn_config_schema.mdx +++ b/api_docs/kbn_config_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-schema title: "@kbn/config-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-schema plugin -date: 2024-05-16 +date: 2024-05-20 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 93755213ba306..282e87f93cf19 100644 --- a/api_docs/kbn_content_management_content_editor.mdx +++ b/api_docs/kbn_content_management_content_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-content-editor title: "@kbn/content-management-content-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-content-editor plugin -date: 2024-05-16 +date: 2024-05-20 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 4402b754c1272..f0730ac49dc9f 100644 --- a/api_docs/kbn_content_management_tabbed_table_list_view.mdx +++ b/api_docs/kbn_content_management_tabbed_table_list_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-tabbed-table-list-view title: "@kbn/content-management-tabbed-table-list-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-tabbed-table-list-view plugin -date: 2024-05-16 +date: 2024-05-20 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 d9d45d6991243..e9dd9c590fe78 100644 --- a/api_docs/kbn_content_management_table_list_view.mdx +++ b/api_docs/kbn_content_management_table_list_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view title: "@kbn/content-management-table-list-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view'] --- import kbnContentManagementTableListViewObj from './kbn_content_management_table_list_view.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view_common.mdx b/api_docs/kbn_content_management_table_list_view_common.mdx index e9857b3581be8..4ea257e9f4693 100644 --- a/api_docs/kbn_content_management_table_list_view_common.mdx +++ b/api_docs/kbn_content_management_table_list_view_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view-common title: "@kbn/content-management-table-list-view-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view-common plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view-common'] --- import kbnContentManagementTableListViewCommonObj from './kbn_content_management_table_list_view_common.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view_table.mdx b/api_docs/kbn_content_management_table_list_view_table.mdx index 8217caf36ae13..eba7e73df28e3 100644 --- a/api_docs/kbn_content_management_table_list_view_table.mdx +++ b/api_docs/kbn_content_management_table_list_view_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view-table title: "@kbn/content-management-table-list-view-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view-table plugin -date: 2024-05-16 +date: 2024-05-20 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.devdocs.json b/api_docs/kbn_content_management_utils.devdocs.json index 13e2736f70f28..f980f0d987f71 100644 --- a/api_docs/kbn_content_management_utils.devdocs.json +++ b/api_docs/kbn_content_management_utils.devdocs.json @@ -2642,6 +2642,22 @@ "path": "packages/kbn-content-management-utils/src/types.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "@kbn/content-management-utils", + "id": "def-common.SavedObjectUpdateOptions.mergeAttributes", + "type": "CompoundType", + "tags": [], + "label": "mergeAttributes", + "description": [ + "\nBy default, update will merge the provided attributes with the ones present on the document\n(performing a standard partial update). Setting this option to `false` will change the behavior, performing\na \"full\" update instead, where the provided attributes will fully replace the existing ones.\nDefaults to `true`." + ], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-content-management-utils/src/types.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -4086,6 +4102,27 @@ "path": "packages/kbn-content-management-utils/src/schema.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "@kbn/content-management-utils", + "id": "def-common.updateOptionsSchema.mergeAttributes", + "type": "Object", + "tags": [], + "label": "mergeAttributes", + "description": [], + "signature": [ + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.Type", + "text": "Type" + }, + "" + ], + "path": "packages/kbn-content-management-utils/src/schema.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/kbn_content_management_utils.mdx b/api_docs/kbn_content_management_utils.mdx index c7b8b882c2215..f5ad856df42f2 100644 --- a/api_docs/kbn_content_management_utils.mdx +++ b/api_docs/kbn_content_management_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-utils title: "@kbn/content-management-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-utils plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-utils'] --- import kbnContentManagementUtilsObj from './kbn_content_management_utils.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 192 | 1 | 126 | 0 | +| 194 | 1 | 127 | 0 | ## Common diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index f0fe7b8c52948..60a7faf24d75e 100644 --- a/api_docs/kbn_core_analytics_browser.mdx +++ b/api_docs/kbn_core_analytics_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser title: "@kbn/core-analytics-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser plugin -date: 2024-05-16 +date: 2024-05-20 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 e71219c579a22..52d781de782fc 100644 --- a/api_docs/kbn_core_analytics_browser_internal.mdx +++ b/api_docs/kbn_core_analytics_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-internal title: "@kbn/core-analytics-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 eadf7948210a1..f1cf5126f992e 100644 --- a/api_docs/kbn_core_analytics_browser_mocks.mdx +++ b/api_docs/kbn_core_analytics_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-mocks title: "@kbn/core-analytics-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 bf2762d21b99e..2f4a695a3ed53 100644 --- a/api_docs/kbn_core_analytics_server.mdx +++ b/api_docs/kbn_core_analytics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server title: "@kbn/core-analytics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server plugin -date: 2024-05-16 +date: 2024-05-20 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 75b6528cb9b2f..eff968fe20d2d 100644 --- a/api_docs/kbn_core_analytics_server_internal.mdx +++ b/api_docs/kbn_core_analytics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-internal title: "@kbn/core-analytics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 690edc92ea703..185158860feee 100644 --- a/api_docs/kbn_core_analytics_server_mocks.mdx +++ b/api_docs/kbn_core_analytics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-mocks title: "@kbn/core-analytics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 a689e6fb469dc..3d0ea8e3ec7d6 100644 --- a/api_docs/kbn_core_application_browser.mdx +++ b/api_docs/kbn_core_application_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser title: "@kbn/core-application-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser plugin -date: 2024-05-16 +date: 2024-05-20 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 e9ed2cae9ffca..ecc47ebaa0cfc 100644 --- a/api_docs/kbn_core_application_browser_internal.mdx +++ b/api_docs/kbn_core_application_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-internal title: "@kbn/core-application-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 78384126a9f3c..c776bc132b635 100644 --- a/api_docs/kbn_core_application_browser_mocks.mdx +++ b/api_docs/kbn_core_application_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-mocks title: "@kbn/core-application-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 0071968858c41..9e4c892189ffa 100644 --- a/api_docs/kbn_core_application_common.mdx +++ b/api_docs/kbn_core_application_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-common title: "@kbn/core-application-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-common plugin -date: 2024-05-16 +date: 2024-05-20 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 b888233b0643d..c7cdaa131350d 100644 --- a/api_docs/kbn_core_apps_browser_internal.mdx +++ b/api_docs/kbn_core_apps_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-internal title: "@kbn/core-apps-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 92697fa71bb4c..8b8ceb5dcdc30 100644 --- a/api_docs/kbn_core_apps_browser_mocks.mdx +++ b/api_docs/kbn_core_apps_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-mocks title: "@kbn/core-apps-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 d705949c429a4..86c9e52e0e3f0 100644 --- a/api_docs/kbn_core_apps_server_internal.mdx +++ b/api_docs/kbn_core_apps_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-server-internal title: "@kbn/core-apps-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-server-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 f549850c9770a..81f88c68b532c 100644 --- a/api_docs/kbn_core_base_browser_mocks.mdx +++ b/api_docs/kbn_core_base_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-browser-mocks title: "@kbn/core-base-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-browser-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 cd6f3e99ffd46..119defe80e8d8 100644 --- a/api_docs/kbn_core_base_common.mdx +++ b/api_docs/kbn_core_base_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-common title: "@kbn/core-base-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-common plugin -date: 2024-05-16 +date: 2024-05-20 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 a6508dd639ae3..c2898e2946b88 100644 --- a/api_docs/kbn_core_base_server_internal.mdx +++ b/api_docs/kbn_core_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-internal title: "@kbn/core-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 9e6d1e512012a..5f13b1c5433d8 100644 --- a/api_docs/kbn_core_base_server_mocks.mdx +++ b/api_docs/kbn_core_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-mocks title: "@kbn/core-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 3725941fd9159..b52877a793a78 100644 --- a/api_docs/kbn_core_capabilities_browser_mocks.mdx +++ b/api_docs/kbn_core_capabilities_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-browser-mocks title: "@kbn/core-capabilities-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-browser-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 069c27e6956ad..893d300a007c7 100644 --- a/api_docs/kbn_core_capabilities_common.mdx +++ b/api_docs/kbn_core_capabilities_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-common title: "@kbn/core-capabilities-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-common plugin -date: 2024-05-16 +date: 2024-05-20 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 69557c67d6398..a79edf2889cc0 100644 --- a/api_docs/kbn_core_capabilities_server.mdx +++ b/api_docs/kbn_core_capabilities_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server title: "@kbn/core-capabilities-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server plugin -date: 2024-05-16 +date: 2024-05-20 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 1b05c031a3c2a..af6110cdeb0d0 100644 --- a/api_docs/kbn_core_capabilities_server_mocks.mdx +++ b/api_docs/kbn_core_capabilities_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server-mocks title: "@kbn/core-capabilities-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server-mocks'] --- import kbnCoreCapabilitiesServerMocksObj from './kbn_core_capabilities_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser.mdx b/api_docs/kbn_core_chrome_browser.mdx index 2938229af5cf6..561f388dc4856 100644 --- a/api_docs/kbn_core_chrome_browser.mdx +++ b/api_docs/kbn_core_chrome_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser title: "@kbn/core-chrome-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser'] --- import kbnCoreChromeBrowserObj from './kbn_core_chrome_browser.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser_mocks.mdx b/api_docs/kbn_core_chrome_browser_mocks.mdx index 55af2a2b04052..6b1c3c2669c58 100644 --- a/api_docs/kbn_core_chrome_browser_mocks.mdx +++ b/api_docs/kbn_core_chrome_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser-mocks title: "@kbn/core-chrome-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 20a905c16d1f7..809ec82628dc2 100644 --- a/api_docs/kbn_core_config_server_internal.mdx +++ b/api_docs/kbn_core_config_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-config-server-internal title: "@kbn/core-config-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-config-server-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 7c5fed8bab505..1888c71dc222e 100644 --- a/api_docs/kbn_core_custom_branding_browser.mdx +++ b/api_docs/kbn_core_custom_branding_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser title: "@kbn/core-custom-branding-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser plugin -date: 2024-05-16 +date: 2024-05-20 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 daef39e0961f0..d872ef0b244f0 100644 --- a/api_docs/kbn_core_custom_branding_browser_internal.mdx +++ b/api_docs/kbn_core_custom_branding_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-internal title: "@kbn/core-custom-branding-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 c5118b4045ccb..8dc1b7e7b1d64 100644 --- a/api_docs/kbn_core_custom_branding_browser_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-mocks title: "@kbn/core-custom-branding-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 16075136e3749..c71457189c7b0 100644 --- a/api_docs/kbn_core_custom_branding_common.mdx +++ b/api_docs/kbn_core_custom_branding_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-common title: "@kbn/core-custom-branding-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-common plugin -date: 2024-05-16 +date: 2024-05-20 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 f1e01b6677e3e..cd134fe1ca899 100644 --- a/api_docs/kbn_core_custom_branding_server.mdx +++ b/api_docs/kbn_core_custom_branding_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server title: "@kbn/core-custom-branding-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server plugin -date: 2024-05-16 +date: 2024-05-20 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 af6e37d34a5c7..30e01ce4f57fe 100644 --- a/api_docs/kbn_core_custom_branding_server_internal.mdx +++ b/api_docs/kbn_core_custom_branding_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-internal title: "@kbn/core-custom-branding-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 2f77ee18b1d37..3dd58bffce2a1 100644 --- a/api_docs/kbn_core_custom_branding_server_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-mocks title: "@kbn/core-custom-branding-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 0868460e0e143..bb4457182fb78 100644 --- a/api_docs/kbn_core_deprecations_browser.mdx +++ b/api_docs/kbn_core_deprecations_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser title: "@kbn/core-deprecations-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser plugin -date: 2024-05-16 +date: 2024-05-20 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 510e1180c8b09..b92742537b0cf 100644 --- a/api_docs/kbn_core_deprecations_browser_internal.mdx +++ b/api_docs/kbn_core_deprecations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-internal title: "@kbn/core-deprecations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 c378305c3f22d..5a8c0b1d390ec 100644 --- a/api_docs/kbn_core_deprecations_browser_mocks.mdx +++ b/api_docs/kbn_core_deprecations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-mocks title: "@kbn/core-deprecations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 a0ca24961ac62..11cbe6769aca8 100644 --- a/api_docs/kbn_core_deprecations_common.mdx +++ b/api_docs/kbn_core_deprecations_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-common title: "@kbn/core-deprecations-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-common plugin -date: 2024-05-16 +date: 2024-05-20 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 781ef5417f39f..4ebdebbcefc41 100644 --- a/api_docs/kbn_core_deprecations_server.mdx +++ b/api_docs/kbn_core_deprecations_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server title: "@kbn/core-deprecations-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server plugin -date: 2024-05-16 +date: 2024-05-20 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 997c63f4a5ca4..d1d60c1286e56 100644 --- a/api_docs/kbn_core_deprecations_server_internal.mdx +++ b/api_docs/kbn_core_deprecations_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-internal title: "@kbn/core-deprecations-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 cdd061bfc78b2..65ddf2ae2bfa3 100644 --- a/api_docs/kbn_core_deprecations_server_mocks.mdx +++ b/api_docs/kbn_core_deprecations_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-mocks title: "@kbn/core-deprecations-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 7499cc93a3cc8..21b4324465695 100644 --- a/api_docs/kbn_core_doc_links_browser.mdx +++ b/api_docs/kbn_core_doc_links_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser title: "@kbn/core-doc-links-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser plugin -date: 2024-05-16 +date: 2024-05-20 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 745a9e38124e1..936e409ff66d3 100644 --- a/api_docs/kbn_core_doc_links_browser_mocks.mdx +++ b/api_docs/kbn_core_doc_links_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser-mocks title: "@kbn/core-doc-links-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 6d91445f12e7c..8b86585b41685 100644 --- a/api_docs/kbn_core_doc_links_server.mdx +++ b/api_docs/kbn_core_doc_links_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server title: "@kbn/core-doc-links-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server plugin -date: 2024-05-16 +date: 2024-05-20 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 ccecbcb9385b0..58cb78c71fe10 100644 --- a/api_docs/kbn_core_doc_links_server_mocks.mdx +++ b/api_docs/kbn_core_doc_links_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server-mocks title: "@kbn/core-doc-links-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 8d7fb99524dc8..8ac84befbf69c 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-internal title: "@kbn/core-elasticsearch-client-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 f08650bad383a..30511a404be73 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-mocks title: "@kbn/core-elasticsearch-client-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 31d7dca79e9c5..d0ff8f1de473f 100644 --- a/api_docs/kbn_core_elasticsearch_server.mdx +++ b/api_docs/kbn_core_elasticsearch_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server title: "@kbn/core-elasticsearch-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server plugin -date: 2024-05-16 +date: 2024-05-20 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.devdocs.json b/api_docs/kbn_core_elasticsearch_server_internal.devdocs.json index 1c0a249cb0f90..3e4e99923088b 100644 --- a/api_docs/kbn_core_elasticsearch_server_internal.devdocs.json +++ b/api_docs/kbn_core_elasticsearch_server_internal.devdocs.json @@ -3114,7 +3114,7 @@ "label": "ElasticsearchConfigType", "description": [], "signature": [ - "{ readonly username?: string | undefined; readonly password?: string | undefined; readonly serviceAccountToken?: string | undefined; readonly ssl: Readonly<{ key?: string | undefined; certificateAuthorities?: string | string[] | undefined; certificate?: string | undefined; keyPassphrase?: string | undefined; } & { verificationMode: \"none\" | \"full\" | \"certificate\"; keystore: Readonly<{ path?: string | undefined; password?: string | undefined; } & {}>; truststore: Readonly<{ path?: string | undefined; password?: string | undefined; } & {}>; alwaysPresentCertificate: boolean; }>; readonly healthCheck: Readonly<{} & { delay: moment.Duration; startupDelay: moment.Duration; }>; readonly hosts: string | string[]; readonly apiVersion: string; readonly customHeaders: Record; readonly sniffOnStart: boolean; readonly sniffInterval: false | moment.Duration; readonly sniffOnConnectionFault: boolean; readonly maxSockets: number; readonly maxIdleSockets: number; readonly idleSocketTimeout: moment.Duration; readonly compression: boolean; readonly requestHeadersWhitelist: string | string[]; readonly shardTimeout: moment.Duration; readonly requestTimeout: moment.Duration; readonly pingTimeout: moment.Duration; readonly logQueries: boolean; readonly ignoreVersionMismatch: boolean; readonly skipStartupConnectionCheck: boolean; readonly apisToRedactInLogs: Readonly<{ method?: string | undefined; } & { path: string; }>[]; }" + "{ readonly username?: string | undefined; readonly password?: string | undefined; readonly serviceAccountToken?: string | undefined; readonly ssl: Readonly<{ key?: string | undefined; certificateAuthorities?: string | string[] | undefined; certificate?: string | undefined; keyPassphrase?: string | undefined; } & { verificationMode: \"none\" | \"full\" | \"certificate\"; keystore: Readonly<{ password?: string | undefined; path?: string | undefined; } & {}>; truststore: Readonly<{ password?: string | undefined; path?: string | undefined; } & {}>; alwaysPresentCertificate: boolean; }>; readonly healthCheck: Readonly<{} & { delay: moment.Duration; startupDelay: moment.Duration; }>; readonly hosts: string | string[]; readonly apiVersion: string; readonly customHeaders: Record; readonly sniffOnStart: boolean; readonly sniffInterval: false | moment.Duration; readonly sniffOnConnectionFault: boolean; readonly maxSockets: number; readonly maxIdleSockets: number; readonly idleSocketTimeout: moment.Duration; readonly compression: boolean; readonly requestHeadersWhitelist: string | string[]; readonly shardTimeout: moment.Duration; readonly requestTimeout: moment.Duration; readonly pingTimeout: moment.Duration; readonly logQueries: boolean; readonly ignoreVersionMismatch: boolean; readonly skipStartupConnectionCheck: boolean; readonly apisToRedactInLogs: Readonly<{ method?: string | undefined; } & { path: string; }>[]; }" ], "path": "packages/core/elasticsearch/core-elasticsearch-server-internal/src/elasticsearch_config.ts", "deprecated": false, diff --git a/api_docs/kbn_core_elasticsearch_server_internal.mdx b/api_docs/kbn_core_elasticsearch_server_internal.mdx index 3bb354475ff7c..d7f6cc3720387 100644 --- a/api_docs/kbn_core_elasticsearch_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-internal title: "@kbn/core-elasticsearch-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 0906d107519e0..fac72a013f43b 100644 --- a/api_docs/kbn_core_elasticsearch_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-mocks title: "@kbn/core-elasticsearch-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 c55c038bc5a74..4127b49887220 100644 --- a/api_docs/kbn_core_environment_server_internal.mdx +++ b/api_docs/kbn_core_environment_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-internal title: "@kbn/core-environment-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 84bcfe562c0d8..cdeced5ebdd5f 100644 --- a/api_docs/kbn_core_environment_server_mocks.mdx +++ b/api_docs/kbn_core_environment_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-mocks title: "@kbn/core-environment-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 03350ba3d06a4..013294e2d7622 100644 --- a/api_docs/kbn_core_execution_context_browser.mdx +++ b/api_docs/kbn_core_execution_context_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser title: "@kbn/core-execution-context-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser plugin -date: 2024-05-16 +date: 2024-05-20 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 8950e76ac4739..8621e8d3c2e2e 100644 --- a/api_docs/kbn_core_execution_context_browser_internal.mdx +++ b/api_docs/kbn_core_execution_context_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-internal title: "@kbn/core-execution-context-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 32aaeea7ed062..69587202b0f55 100644 --- a/api_docs/kbn_core_execution_context_browser_mocks.mdx +++ b/api_docs/kbn_core_execution_context_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-mocks title: "@kbn/core-execution-context-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 508e18c979d2b..2c43e99c5be70 100644 --- a/api_docs/kbn_core_execution_context_common.mdx +++ b/api_docs/kbn_core_execution_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-common title: "@kbn/core-execution-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-common plugin -date: 2024-05-16 +date: 2024-05-20 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 bd6a6c7b30ce1..650c2c5883b56 100644 --- a/api_docs/kbn_core_execution_context_server.mdx +++ b/api_docs/kbn_core_execution_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server title: "@kbn/core-execution-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server plugin -date: 2024-05-16 +date: 2024-05-20 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 824ceb81a22d3..58ee74f837063 100644 --- a/api_docs/kbn_core_execution_context_server_internal.mdx +++ b/api_docs/kbn_core_execution_context_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-internal title: "@kbn/core-execution-context-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 fe462fd32358d..a1a31922adb06 100644 --- a/api_docs/kbn_core_execution_context_server_mocks.mdx +++ b/api_docs/kbn_core_execution_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-mocks title: "@kbn/core-execution-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 bd3e38f873af2..81ffc36c9db11 100644 --- a/api_docs/kbn_core_fatal_errors_browser.mdx +++ b/api_docs/kbn_core_fatal_errors_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser title: "@kbn/core-fatal-errors-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser plugin -date: 2024-05-16 +date: 2024-05-20 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 71a814a919c61..d79fa0cc8cb87 100644 --- a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx +++ b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser-mocks title: "@kbn/core-fatal-errors-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 74d54f95b306f..2ffc1dac97282 100644 --- a/api_docs/kbn_core_http_browser.mdx +++ b/api_docs/kbn_core_http_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser title: "@kbn/core-http-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser plugin -date: 2024-05-16 +date: 2024-05-20 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 d4e471d6c15c1..51ab111ca359b 100644 --- a/api_docs/kbn_core_http_browser_internal.mdx +++ b/api_docs/kbn_core_http_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-internal title: "@kbn/core-http-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 13fc9e5b743ad..426ce00b4bd15 100644 --- a/api_docs/kbn_core_http_browser_mocks.mdx +++ b/api_docs/kbn_core_http_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-mocks title: "@kbn/core-http-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 9cb9cc0201c09..9d396f7034a82 100644 --- a/api_docs/kbn_core_http_common.mdx +++ b/api_docs/kbn_core_http_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-common title: "@kbn/core-http-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-common plugin -date: 2024-05-16 +date: 2024-05-20 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 08190d1483573..f1d3850165adb 100644 --- a/api_docs/kbn_core_http_context_server_mocks.mdx +++ b/api_docs/kbn_core_http_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-context-server-mocks title: "@kbn/core-http-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-context-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 b2e5c49847b70..41b759d54336b 100644 --- a/api_docs/kbn_core_http_request_handler_context_server.mdx +++ b/api_docs/kbn_core_http_request_handler_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-request-handler-context-server title: "@kbn/core-http-request-handler-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-request-handler-context-server plugin -date: 2024-05-16 +date: 2024-05-20 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 143757c9a5c52..510eb73db7c09 100644 --- a/api_docs/kbn_core_http_resources_server.mdx +++ b/api_docs/kbn_core_http_resources_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server title: "@kbn/core-http-resources-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server plugin -date: 2024-05-16 +date: 2024-05-20 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 e800eb4157bc3..c5f3f2ba85268 100644 --- a/api_docs/kbn_core_http_resources_server_internal.mdx +++ b/api_docs/kbn_core_http_resources_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-internal title: "@kbn/core-http-resources-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 c663c3c117c52..f6791e889e18f 100644 --- a/api_docs/kbn_core_http_resources_server_mocks.mdx +++ b/api_docs/kbn_core_http_resources_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-mocks title: "@kbn/core-http-resources-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 d748890aaa800..32b1a6dcafc43 100644 --- a/api_docs/kbn_core_http_router_server_internal.mdx +++ b/api_docs/kbn_core_http_router_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-internal title: "@kbn/core-http-router-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 a6931d53482ab..f5febc6f53764 100644 --- a/api_docs/kbn_core_http_router_server_mocks.mdx +++ b/api_docs/kbn_core_http_router_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-mocks title: "@kbn/core-http-router-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 35e4843e88499..33341437b0c70 100644 --- a/api_docs/kbn_core_http_server.devdocs.json +++ b/api_docs/kbn_core_http_server.devdocs.json @@ -3632,11 +3632,11 @@ }, { "plugin": "security", - "path": "x-pack/plugins/security/server/routes/indices/get_fields.ts" + "path": "x-pack/plugins/security/server/routes/feature_check/feature_check.ts" }, { "plugin": "security", - "path": "x-pack/plugins/security/server/routes/role_mapping/feature_check.ts" + "path": "x-pack/plugins/security/server/routes/indices/get_fields.ts" }, { "plugin": "security", @@ -4574,6 +4574,10 @@ "plugin": "serverlessSearch", "path": "x-pack/plugins/serverless_search/server/routes/mapping_routes.ts" }, + { + "plugin": "serverlessSearch", + "path": "x-pack/plugins/serverless_search/server/routes/ingest_pipeline_routes.ts" + }, { "plugin": "snapshotRestore", "path": "x-pack/plugins/snapshot_restore/server/routes/api/app.ts" @@ -5824,11 +5828,11 @@ }, { "plugin": "security", - "path": "x-pack/plugins/security/server/routes/indices/get_fields.test.ts" + "path": "x-pack/plugins/security/server/routes/feature_check/feature_check.test.ts" }, { "plugin": "security", - "path": "x-pack/plugins/security/server/routes/role_mapping/feature_check.test.ts" + "path": "x-pack/plugins/security/server/routes/indices/get_fields.test.ts" }, { "plugin": "security", @@ -7240,6 +7244,14 @@ "plugin": "assetManager", "path": "x-pack/plugins/observability_solution/asset_manager/server/routes/sample_assets.ts" }, + { + "plugin": "assetManager", + "path": "x-pack/plugins/observability_solution/asset_manager/server/routes/entities/create.ts" + }, + { + "plugin": "assetManager", + "path": "x-pack/plugins/observability_solution/asset_manager/server/routes/entities/reset.ts" + }, { "plugin": "profiling", "path": "x-pack/plugins/observability_solution/profiling/server/routes/setup/route.ts" @@ -9658,6 +9670,10 @@ "plugin": "assetManager", "path": "x-pack/plugins/observability_solution/asset_manager/server/routes/sample_assets.ts" }, + { + "plugin": "assetManager", + "path": "x-pack/plugins/observability_solution/asset_manager/server/routes/entities/delete.ts" + }, { "plugin": "synthetics", "path": "x-pack/plugins/observability_solution/synthetics/server/server.ts" @@ -13989,7 +14005,7 @@ }, { "plugin": "ecsDataQualityDashboard", - "path": "x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results.ts" + "path": "x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results_indices_latest.ts" }, { "plugin": "ml", diff --git a/api_docs/kbn_core_http_server.mdx b/api_docs/kbn_core_http_server.mdx index 2f0741b73882e..cfd001143db62 100644 --- a/api_docs/kbn_core_http_server.mdx +++ b/api_docs/kbn_core_http_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server title: "@kbn/core-http-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server plugin -date: 2024-05-16 +date: 2024-05-20 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.devdocs.json b/api_docs/kbn_core_http_server_internal.devdocs.json index 6d7734efc14d9..96a3c4d50cdb2 100644 --- a/api_docs/kbn_core_http_server_internal.devdocs.json +++ b/api_docs/kbn_core_http_server_internal.devdocs.json @@ -1449,7 +1449,7 @@ "label": "HttpConfigType", "description": [], "signature": [ - "{ readonly uuid?: string | undefined; readonly basePath?: string | undefined; readonly publicBaseUrl?: string | undefined; readonly name: string; readonly ssl: Readonly<{ key?: string | undefined; certificateAuthorities?: string | string[] | undefined; certificate?: string | undefined; keyPassphrase?: string | undefined; redirectHttpFromPort?: number | undefined; } & { enabled: boolean; keystore: Readonly<{ path?: string | undefined; password?: string | undefined; } & {}>; truststore: Readonly<{ path?: string | undefined; password?: string | undefined; } & {}>; cipherSuites: string[]; supportedProtocols: string[]; clientAuthentication: \"none\" | \"optional\" | \"required\"; }>; readonly host: string; readonly port: number; readonly compression: Readonly<{ referrerWhitelist?: string[] | undefined; } & { enabled: boolean; brotli: Readonly<{} & { enabled: boolean; quality: number; }>; }>; readonly cors: Readonly<{} & { enabled: boolean; allowCredentials: boolean; allowOrigin: string[] | \"*\"[]; }>; readonly versioned: Readonly<{} & { useVersionResolutionStrategyForInternalPaths: string[]; versionResolution: \"none\" | \"oldest\" | \"newest\"; strictClientVersionCheck: boolean; }>; readonly autoListen: boolean; readonly shutdownTimeout: moment.Duration; readonly cdn: Readonly<{ url?: string | undefined; } & {}>; readonly oas: Readonly<{} & { enabled: boolean; }>; readonly securityResponseHeaders: Readonly<{} & { referrerPolicy: \"origin\" | \"no-referrer\" | \"no-referrer-when-downgrade\" | \"origin-when-cross-origin\" | \"same-origin\" | \"strict-origin\" | \"strict-origin-when-cross-origin\" | \"unsafe-url\" | null; strictTransportSecurity: string | null; xContentTypeOptions: \"nosniff\" | null; permissionsPolicy: string | null; disableEmbedding: boolean; crossOriginOpenerPolicy: \"same-origin\" | \"unsafe-none\" | \"same-origin-allow-popups\" | null; }>; readonly customResponseHeaders: Record; readonly maxPayload: ", + "{ readonly uuid?: string | undefined; readonly basePath?: string | undefined; readonly publicBaseUrl?: string | undefined; readonly name: string; readonly ssl: Readonly<{ key?: string | undefined; certificateAuthorities?: string | string[] | undefined; certificate?: string | undefined; keyPassphrase?: string | undefined; redirectHttpFromPort?: number | undefined; } & { enabled: boolean; keystore: Readonly<{ password?: string | undefined; path?: string | undefined; } & {}>; truststore: Readonly<{ password?: string | undefined; path?: string | undefined; } & {}>; cipherSuites: string[]; supportedProtocols: string[]; clientAuthentication: \"none\" | \"optional\" | \"required\"; }>; readonly host: string; readonly port: number; readonly compression: Readonly<{ referrerWhitelist?: string[] | undefined; } & { enabled: boolean; brotli: Readonly<{} & { enabled: boolean; quality: number; }>; }>; readonly cors: Readonly<{} & { enabled: boolean; allowCredentials: boolean; allowOrigin: string[] | \"*\"[]; }>; readonly versioned: Readonly<{} & { useVersionResolutionStrategyForInternalPaths: string[]; versionResolution: \"none\" | \"oldest\" | \"newest\"; strictClientVersionCheck: boolean; }>; readonly autoListen: boolean; readonly shutdownTimeout: moment.Duration; readonly cdn: Readonly<{ url?: string | undefined; } & {}>; readonly oas: Readonly<{} & { enabled: boolean; }>; readonly securityResponseHeaders: Readonly<{} & { referrerPolicy: \"origin\" | \"no-referrer\" | \"no-referrer-when-downgrade\" | \"origin-when-cross-origin\" | \"same-origin\" | \"strict-origin\" | \"strict-origin-when-cross-origin\" | \"unsafe-url\" | null; strictTransportSecurity: string | null; xContentTypeOptions: \"nosniff\" | null; permissionsPolicy: string | null; disableEmbedding: boolean; crossOriginOpenerPolicy: \"same-origin\" | \"unsafe-none\" | \"same-origin-allow-popups\" | null; }>; readonly customResponseHeaders: Record; readonly maxPayload: ", { "pluginId": "@kbn/config-schema", "scope": "common", diff --git a/api_docs/kbn_core_http_server_internal.mdx b/api_docs/kbn_core_http_server_internal.mdx index 74d82823865ac..f12330784e868 100644 --- a/api_docs/kbn_core_http_server_internal.mdx +++ b/api_docs/kbn_core_http_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-internal title: "@kbn/core-http-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-internal plugin -date: 2024-05-16 +date: 2024-05-20 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.devdocs.json b/api_docs/kbn_core_http_server_mocks.devdocs.json index 780df3a7f05fc..ce821d09edbc7 100644 --- a/api_docs/kbn_core_http_server_mocks.devdocs.json +++ b/api_docs/kbn_core_http_server_mocks.devdocs.json @@ -27,7 +27,7 @@ "label": "createConfigService", "description": [], "signature": [ - "({ server, externalUrl, csp, }?: Partial<{ server: Partial; truststore: Readonly<{ path?: string | undefined; password?: string | undefined; } & {}>; cipherSuites: string[]; supportedProtocols: string[]; clientAuthentication: \"none\" | \"optional\" | \"required\"; }>; host: string; port: number; compression: Readonly<{ referrerWhitelist?: string[] | undefined; } & { enabled: boolean; brotli: Readonly<{} & { enabled: boolean; quality: number; }>; }>; cors: Readonly<{} & { enabled: boolean; allowCredentials: boolean; allowOrigin: string[] | \"*\"[]; }>; versioned: Readonly<{} & { useVersionResolutionStrategyForInternalPaths: string[]; versionResolution: \"none\" | \"oldest\" | \"newest\"; strictClientVersionCheck: boolean; }>; autoListen: boolean; shutdownTimeout: moment.Duration; cdn: Readonly<{ url?: string | undefined; } & {}>; oas: Readonly<{} & { enabled: boolean; }>; securityResponseHeaders: Readonly<{} & { referrerPolicy: \"origin\" | \"no-referrer\" | \"no-referrer-when-downgrade\" | \"origin-when-cross-origin\" | \"same-origin\" | \"strict-origin\" | \"strict-origin-when-cross-origin\" | \"unsafe-url\" | null; strictTransportSecurity: string | null; xContentTypeOptions: \"nosniff\" | null; permissionsPolicy: string | null; disableEmbedding: boolean; crossOriginOpenerPolicy: \"same-origin\" | \"unsafe-none\" | \"same-origin-allow-popups\" | null; }>; customResponseHeaders: Record; maxPayload: ", + "({ server, externalUrl, csp, }?: Partial<{ server: Partial; truststore: Readonly<{ password?: string | undefined; path?: string | undefined; } & {}>; cipherSuites: string[]; supportedProtocols: string[]; clientAuthentication: \"none\" | \"optional\" | \"required\"; }>; host: string; port: number; compression: Readonly<{ referrerWhitelist?: string[] | undefined; } & { enabled: boolean; brotli: Readonly<{} & { enabled: boolean; quality: number; }>; }>; cors: Readonly<{} & { enabled: boolean; allowCredentials: boolean; allowOrigin: string[] | \"*\"[]; }>; versioned: Readonly<{} & { useVersionResolutionStrategyForInternalPaths: string[]; versionResolution: \"none\" | \"oldest\" | \"newest\"; strictClientVersionCheck: boolean; }>; autoListen: boolean; shutdownTimeout: moment.Duration; cdn: Readonly<{ url?: string | undefined; } & {}>; oas: Readonly<{} & { enabled: boolean; }>; securityResponseHeaders: Readonly<{} & { referrerPolicy: \"origin\" | \"no-referrer\" | \"no-referrer-when-downgrade\" | \"origin-when-cross-origin\" | \"same-origin\" | \"strict-origin\" | \"strict-origin-when-cross-origin\" | \"unsafe-url\" | null; strictTransportSecurity: string | null; xContentTypeOptions: \"nosniff\" | null; permissionsPolicy: string | null; disableEmbedding: boolean; crossOriginOpenerPolicy: \"same-origin\" | \"unsafe-none\" | \"same-origin-allow-popups\" | null; }>; customResponseHeaders: Record; maxPayload: ", { "pluginId": "@kbn/config-schema", "scope": "common", @@ -64,7 +64,7 @@ "label": "{\n server,\n externalUrl,\n csp,\n}", "description": [], "signature": [ - "Partial<{ server: Partial; truststore: Readonly<{ path?: string | undefined; password?: string | undefined; } & {}>; cipherSuites: string[]; supportedProtocols: string[]; clientAuthentication: \"none\" | \"optional\" | \"required\"; }>; host: string; port: number; compression: Readonly<{ referrerWhitelist?: string[] | undefined; } & { enabled: boolean; brotli: Readonly<{} & { enabled: boolean; quality: number; }>; }>; cors: Readonly<{} & { enabled: boolean; allowCredentials: boolean; allowOrigin: string[] | \"*\"[]; }>; versioned: Readonly<{} & { useVersionResolutionStrategyForInternalPaths: string[]; versionResolution: \"none\" | \"oldest\" | \"newest\"; strictClientVersionCheck: boolean; }>; autoListen: boolean; shutdownTimeout: moment.Duration; cdn: Readonly<{ url?: string | undefined; } & {}>; oas: Readonly<{} & { enabled: boolean; }>; securityResponseHeaders: Readonly<{} & { referrerPolicy: \"origin\" | \"no-referrer\" | \"no-referrer-when-downgrade\" | \"origin-when-cross-origin\" | \"same-origin\" | \"strict-origin\" | \"strict-origin-when-cross-origin\" | \"unsafe-url\" | null; strictTransportSecurity: string | null; xContentTypeOptions: \"nosniff\" | null; permissionsPolicy: string | null; disableEmbedding: boolean; crossOriginOpenerPolicy: \"same-origin\" | \"unsafe-none\" | \"same-origin-allow-popups\" | null; }>; customResponseHeaders: Record; maxPayload: ", + "Partial<{ server: Partial; truststore: Readonly<{ password?: string | undefined; path?: string | undefined; } & {}>; cipherSuites: string[]; supportedProtocols: string[]; clientAuthentication: \"none\" | \"optional\" | \"required\"; }>; host: string; port: number; compression: Readonly<{ referrerWhitelist?: string[] | undefined; } & { enabled: boolean; brotli: Readonly<{} & { enabled: boolean; quality: number; }>; }>; cors: Readonly<{} & { enabled: boolean; allowCredentials: boolean; allowOrigin: string[] | \"*\"[]; }>; versioned: Readonly<{} & { useVersionResolutionStrategyForInternalPaths: string[]; versionResolution: \"none\" | \"oldest\" | \"newest\"; strictClientVersionCheck: boolean; }>; autoListen: boolean; shutdownTimeout: moment.Duration; cdn: Readonly<{ url?: string | undefined; } & {}>; oas: Readonly<{} & { enabled: boolean; }>; securityResponseHeaders: Readonly<{} & { referrerPolicy: \"origin\" | \"no-referrer\" | \"no-referrer-when-downgrade\" | \"origin-when-cross-origin\" | \"same-origin\" | \"strict-origin\" | \"strict-origin-when-cross-origin\" | \"unsafe-url\" | null; strictTransportSecurity: string | null; xContentTypeOptions: \"nosniff\" | null; permissionsPolicy: string | null; disableEmbedding: boolean; crossOriginOpenerPolicy: \"same-origin\" | \"unsafe-none\" | \"same-origin-allow-popups\" | null; }>; customResponseHeaders: Record; maxPayload: ", { "pluginId": "@kbn/config-schema", "scope": "common", diff --git a/api_docs/kbn_core_http_server_mocks.mdx b/api_docs/kbn_core_http_server_mocks.mdx index 71d2d9056a41a..ae39c8aa3f7a7 100644 --- a/api_docs/kbn_core_http_server_mocks.mdx +++ b/api_docs/kbn_core_http_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-mocks title: "@kbn/core-http-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 01a0c79507592..23a25a57d3eb2 100644 --- a/api_docs/kbn_core_i18n_browser.mdx +++ b/api_docs/kbn_core_i18n_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser title: "@kbn/core-i18n-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser plugin -date: 2024-05-16 +date: 2024-05-20 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 7d0404dc61a8e..8ad00b1a48823 100644 --- a/api_docs/kbn_core_i18n_browser_mocks.mdx +++ b/api_docs/kbn_core_i18n_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser-mocks title: "@kbn/core-i18n-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 bb2353baf29cb..32cd401da773a 100644 --- a/api_docs/kbn_core_i18n_server.mdx +++ b/api_docs/kbn_core_i18n_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server title: "@kbn/core-i18n-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server plugin -date: 2024-05-16 +date: 2024-05-20 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 c8b381e62b708..0854f4c38ea98 100644 --- a/api_docs/kbn_core_i18n_server_internal.mdx +++ b/api_docs/kbn_core_i18n_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-internal title: "@kbn/core-i18n-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 e7e2f8282fa68..7f47ab316c544 100644 --- a/api_docs/kbn_core_i18n_server_mocks.mdx +++ b/api_docs/kbn_core_i18n_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-mocks title: "@kbn/core-i18n-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 cbac2e832e608..34a1c90381629 100644 --- a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx +++ b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser-mocks title: "@kbn/core-injected-metadata-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-injected-metadata-browser-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 c45db8af105ae..9c91f7b5c652e 100644 --- a/api_docs/kbn_core_integrations_browser_internal.mdx +++ b/api_docs/kbn_core_integrations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-internal title: "@kbn/core-integrations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 dbf54f2de2589..84709ef3d39ae 100644 --- a/api_docs/kbn_core_integrations_browser_mocks.mdx +++ b/api_docs/kbn_core_integrations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-mocks title: "@kbn/core-integrations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 6b24171594fd6..1b414a10e89b7 100644 --- a/api_docs/kbn_core_lifecycle_browser.mdx +++ b/api_docs/kbn_core_lifecycle_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser title: "@kbn/core-lifecycle-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser plugin -date: 2024-05-16 +date: 2024-05-20 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 a6f3e1349b4e6..de22679913fb2 100644 --- a/api_docs/kbn_core_lifecycle_browser_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser-mocks title: "@kbn/core-lifecycle-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 d28c80ddfbd2e..d25d1d6e7ca21 100644 --- a/api_docs/kbn_core_lifecycle_server.mdx +++ b/api_docs/kbn_core_lifecycle_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server title: "@kbn/core-lifecycle-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server plugin -date: 2024-05-16 +date: 2024-05-20 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 0598d9c2b8db2..ee4919ebf5e8a 100644 --- a/api_docs/kbn_core_lifecycle_server_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server-mocks title: "@kbn/core-lifecycle-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 754d285784bf9..1714bfd184514 100644 --- a/api_docs/kbn_core_logging_browser_mocks.mdx +++ b/api_docs/kbn_core_logging_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-browser-mocks title: "@kbn/core-logging-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-browser-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 590893a90cb57..1880edca44f26 100644 --- a/api_docs/kbn_core_logging_common_internal.mdx +++ b/api_docs/kbn_core_logging_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-common-internal title: "@kbn/core-logging-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-common-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 c8a2b600eaa04..c552d5b5c66d8 100644 --- a/api_docs/kbn_core_logging_server.mdx +++ b/api_docs/kbn_core_logging_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server title: "@kbn/core-logging-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server plugin -date: 2024-05-16 +date: 2024-05-20 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 6b249c791d4ec..e23a75f178d16 100644 --- a/api_docs/kbn_core_logging_server_internal.mdx +++ b/api_docs/kbn_core_logging_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-internal title: "@kbn/core-logging-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 2989480912a13..fa24293b7e312 100644 --- a/api_docs/kbn_core_logging_server_mocks.mdx +++ b/api_docs/kbn_core_logging_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-mocks title: "@kbn/core-logging-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 e836d03adce60..f3b2b9e9767aa 100644 --- a/api_docs/kbn_core_metrics_collectors_server_internal.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-internal title: "@kbn/core-metrics-collectors-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 65344413b0abb..0d9a4d1466990 100644 --- a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-mocks title: "@kbn/core-metrics-collectors-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 c8bf769ee1c0a..6d5a874cdcab8 100644 --- a/api_docs/kbn_core_metrics_server.mdx +++ b/api_docs/kbn_core_metrics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server title: "@kbn/core-metrics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server plugin -date: 2024-05-16 +date: 2024-05-20 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 88cd5aad5e94b..b3f6385ab4c76 100644 --- a/api_docs/kbn_core_metrics_server_internal.mdx +++ b/api_docs/kbn_core_metrics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-internal title: "@kbn/core-metrics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 0ba5087c39f72..74375c8683d14 100644 --- a/api_docs/kbn_core_metrics_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-mocks title: "@kbn/core-metrics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 3269c9a08ec78..f40af57c33616 100644 --- a/api_docs/kbn_core_mount_utils_browser.mdx +++ b/api_docs/kbn_core_mount_utils_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-mount-utils-browser title: "@kbn/core-mount-utils-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-mount-utils-browser plugin -date: 2024-05-16 +date: 2024-05-20 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 ba40766b91324..0d23253462dfb 100644 --- a/api_docs/kbn_core_node_server.mdx +++ b/api_docs/kbn_core_node_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server title: "@kbn/core-node-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server plugin -date: 2024-05-16 +date: 2024-05-20 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 efcc8448e2f08..3687e2abd36c7 100644 --- a/api_docs/kbn_core_node_server_internal.mdx +++ b/api_docs/kbn_core_node_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-internal title: "@kbn/core-node-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 163ccd3de0a5c..394035fd76cfd 100644 --- a/api_docs/kbn_core_node_server_mocks.mdx +++ b/api_docs/kbn_core_node_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-mocks title: "@kbn/core-node-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 724f3304476e3..79b00ba864792 100644 --- a/api_docs/kbn_core_notifications_browser.mdx +++ b/api_docs/kbn_core_notifications_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser title: "@kbn/core-notifications-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser plugin -date: 2024-05-16 +date: 2024-05-20 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 2a5904ad34273..64b82ef3698b9 100644 --- a/api_docs/kbn_core_notifications_browser_internal.mdx +++ b/api_docs/kbn_core_notifications_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-internal title: "@kbn/core-notifications-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 f91de95813ed9..d754835110cb8 100644 --- a/api_docs/kbn_core_notifications_browser_mocks.mdx +++ b/api_docs/kbn_core_notifications_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-mocks title: "@kbn/core-notifications-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 32205b46dd043..477ae5b057835 100644 --- a/api_docs/kbn_core_overlays_browser.mdx +++ b/api_docs/kbn_core_overlays_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser title: "@kbn/core-overlays-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser plugin -date: 2024-05-16 +date: 2024-05-20 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 792172a4c18e8..3327fa9b318ae 100644 --- a/api_docs/kbn_core_overlays_browser_internal.mdx +++ b/api_docs/kbn_core_overlays_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-internal title: "@kbn/core-overlays-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 8c14d60c0fbc6..c657290d23f3b 100644 --- a/api_docs/kbn_core_overlays_browser_mocks.mdx +++ b/api_docs/kbn_core_overlays_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-mocks title: "@kbn/core-overlays-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 0b8b77c7af6a0..f87edc5118822 100644 --- a/api_docs/kbn_core_plugins_browser.mdx +++ b/api_docs/kbn_core_plugins_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser title: "@kbn/core-plugins-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser plugin -date: 2024-05-16 +date: 2024-05-20 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 bf6c4692d7885..6a85a491636be 100644 --- a/api_docs/kbn_core_plugins_browser_mocks.mdx +++ b/api_docs/kbn_core_plugins_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser-mocks title: "@kbn/core-plugins-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser-mocks plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser-mocks'] --- import kbnCorePluginsBrowserMocksObj from './kbn_core_plugins_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_contracts_browser.mdx b/api_docs/kbn_core_plugins_contracts_browser.mdx index ec79b1c0d06ec..a147aae398d78 100644 --- a/api_docs/kbn_core_plugins_contracts_browser.mdx +++ b/api_docs/kbn_core_plugins_contracts_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-contracts-browser title: "@kbn/core-plugins-contracts-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-contracts-browser plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-contracts-browser'] --- import kbnCorePluginsContractsBrowserObj from './kbn_core_plugins_contracts_browser.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_contracts_server.mdx b/api_docs/kbn_core_plugins_contracts_server.mdx index 12d9a451b5251..41d446c49fd61 100644 --- a/api_docs/kbn_core_plugins_contracts_server.mdx +++ b/api_docs/kbn_core_plugins_contracts_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-contracts-server title: "@kbn/core-plugins-contracts-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-contracts-server plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-contracts-server'] --- import kbnCorePluginsContractsServerObj from './kbn_core_plugins_contracts_server.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server.mdx b/api_docs/kbn_core_plugins_server.mdx index 3dbccd7d57226..b088e990af2aa 100644 --- a/api_docs/kbn_core_plugins_server.mdx +++ b/api_docs/kbn_core_plugins_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server title: "@kbn/core-plugins-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server plugin -date: 2024-05-16 +date: 2024-05-20 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 983a333cdfd89..e93e9f10e0655 100644 --- a/api_docs/kbn_core_plugins_server_mocks.mdx +++ b/api_docs/kbn_core_plugins_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server-mocks title: "@kbn/core-plugins-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 28651dda40c51..6f5cdb2cc81af 100644 --- a/api_docs/kbn_core_preboot_server.mdx +++ b/api_docs/kbn_core_preboot_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server title: "@kbn/core-preboot-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server plugin -date: 2024-05-16 +date: 2024-05-20 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 942f49342587b..c7620c8dab10a 100644 --- a/api_docs/kbn_core_preboot_server_mocks.mdx +++ b/api_docs/kbn_core_preboot_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server-mocks title: "@kbn/core-preboot-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 614849ea3a7c3..983f50bf92863 100644 --- a/api_docs/kbn_core_rendering_browser_mocks.mdx +++ b/api_docs/kbn_core_rendering_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-browser-mocks title: "@kbn/core-rendering-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-browser-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 1f0294bedf94e..8edd076bae49f 100644 --- a/api_docs/kbn_core_rendering_server_internal.mdx +++ b/api_docs/kbn_core_rendering_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-internal title: "@kbn/core-rendering-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 7bae2adb00007..a6c75edb284e3 100644 --- a/api_docs/kbn_core_rendering_server_mocks.mdx +++ b/api_docs/kbn_core_rendering_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-mocks title: "@kbn/core-rendering-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 beee3376da0a7..646f86ddffb1d 100644 --- a/api_docs/kbn_core_root_server_internal.mdx +++ b/api_docs/kbn_core_root_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-root-server-internal title: "@kbn/core-root-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-root-server-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 173e104c2bada..d054658727353 100644 --- a/api_docs/kbn_core_saved_objects_api_browser.mdx +++ b/api_docs/kbn_core_saved_objects_api_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-browser title: "@kbn/core-saved-objects-api-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-browser plugin -date: 2024-05-16 +date: 2024-05-20 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 7501f41dac5f6..bf16f75d077d5 100644 --- a/api_docs/kbn_core_saved_objects_api_server.mdx +++ b/api_docs/kbn_core_saved_objects_api_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server title: "@kbn/core-saved-objects-api-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server plugin -date: 2024-05-16 +date: 2024-05-20 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 d89c3daddb95c..69104f882c95b 100644 --- a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-mocks title: "@kbn/core-saved-objects-api-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-mocks'] --- import kbnCoreSavedObjectsApiServerMocksObj from './kbn_core_saved_objects_api_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_internal.devdocs.json b/api_docs/kbn_core_saved_objects_base_server_internal.devdocs.json index 22752cea44fca..a7c515398843f 100644 --- a/api_docs/kbn_core_saved_objects_base_server_internal.devdocs.json +++ b/api_docs/kbn_core_saved_objects_base_server_internal.devdocs.json @@ -2601,6 +2601,17 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.HASH_TO_VERSION_MAP.endpointunifieduserartifactmanifest393c6e4f5f16288c24ef9057e4d76a4c", + "type": "string", + "tags": [], + "label": "'endpoint:unified-user-artifact-manifest|393c6e4f5f16288c24ef9057e4d76a4c'", + "description": [], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/constants.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "@kbn/core-saved-objects-base-server-internal", "id": "def-common.HASH_TO_VERSION_MAP.endpointuserartifactmanifest7502b5c5bc923abe8aa5ccfd636e8c3d", 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 8d4b1bada6a17..1215cbdf6be48 100644 --- a/api_docs/kbn_core_saved_objects_base_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-internal title: "@kbn/core-saved-objects-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-internal plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-internal'] --- import kbnCoreSavedObjectsBaseServerInternalObj from './kbn_core_saved_objects_base_server_internal.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 225 | 0 | 182 | 11 | +| 226 | 0 | 183 | 11 | ## Common diff --git a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx index ad76fb69c6b3e..c684ae28d93d7 100644 --- a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-mocks title: "@kbn/core-saved-objects-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 a41ef025bc393..873f0c5a8ca7c 100644 --- a/api_docs/kbn_core_saved_objects_browser.mdx +++ b/api_docs/kbn_core_saved_objects_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser title: "@kbn/core-saved-objects-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser plugin -date: 2024-05-16 +date: 2024-05-20 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 eea26e1d6f2a8..558e408949bc1 100644 --- a/api_docs/kbn_core_saved_objects_browser_internal.mdx +++ b/api_docs/kbn_core_saved_objects_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-internal title: "@kbn/core-saved-objects-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 ee912df1d5ade..385b9057258c1 100644 --- a/api_docs/kbn_core_saved_objects_browser_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-mocks title: "@kbn/core-saved-objects-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 d675cdeb9ee42..0567167730911 100644 --- a/api_docs/kbn_core_saved_objects_common.mdx +++ b/api_docs/kbn_core_saved_objects_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-common title: "@kbn/core-saved-objects-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-common plugin -date: 2024-05-16 +date: 2024-05-20 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 3e50096fc8a58..787138c6f15cf 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-internal title: "@kbn/core-saved-objects-import-export-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 949b094522ef1..af20d6a5b716a 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-mocks title: "@kbn/core-saved-objects-import-export-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-import-export-server-mocks'] --- import kbnCoreSavedObjectsImportExportServerMocksObj from './kbn_core_saved_objects_import_export_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx index 2177b1b8373e5..547e8c067421e 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-internal title: "@kbn/core-saved-objects-migration-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-internal plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-internal'] --- import kbnCoreSavedObjectsMigrationServerInternalObj from './kbn_core_saved_objects_migration_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx index 0a061496ea2dc..afc85c60d3b77 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-mocks title: "@kbn/core-saved-objects-migration-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 9f977ef8a647e..7a316b93c571d 100644 --- a/api_docs/kbn_core_saved_objects_server.mdx +++ b/api_docs/kbn_core_saved_objects_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server title: "@kbn/core-saved-objects-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server plugin -date: 2024-05-16 +date: 2024-05-20 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 bb4d385dd23d1..3d4f73061547b 100644 --- a/api_docs/kbn_core_saved_objects_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-internal title: "@kbn/core-saved-objects-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 3c4ca5fb11135..9075e80015302 100644 --- a/api_docs/kbn_core_saved_objects_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-mocks title: "@kbn/core-saved-objects-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 5985c2bb53d43..4cd03e888abab 100644 --- a/api_docs/kbn_core_saved_objects_utils_server.mdx +++ b/api_docs/kbn_core_saved_objects_utils_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-utils-server title: "@kbn/core-saved-objects-utils-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-utils-server plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-utils-server'] --- import kbnCoreSavedObjectsUtilsServerObj from './kbn_core_saved_objects_utils_server.devdocs.json'; diff --git a/api_docs/kbn_core_security_browser.mdx b/api_docs/kbn_core_security_browser.mdx index 4ae30030a7bcc..a130b207b281d 100644 --- a/api_docs/kbn_core_security_browser.mdx +++ b/api_docs/kbn_core_security_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-browser title: "@kbn/core-security-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-browser plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-browser'] --- import kbnCoreSecurityBrowserObj from './kbn_core_security_browser.devdocs.json'; diff --git a/api_docs/kbn_core_security_browser_internal.mdx b/api_docs/kbn_core_security_browser_internal.mdx index 464dbeeb78c55..886714cdf8b5c 100644 --- a/api_docs/kbn_core_security_browser_internal.mdx +++ b/api_docs/kbn_core_security_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-browser-internal title: "@kbn/core-security-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-browser-internal plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-browser-internal'] --- import kbnCoreSecurityBrowserInternalObj from './kbn_core_security_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_security_browser_mocks.mdx b/api_docs/kbn_core_security_browser_mocks.mdx index 1bfe8202f819d..9a4e4ab30250c 100644 --- a/api_docs/kbn_core_security_browser_mocks.mdx +++ b/api_docs/kbn_core_security_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-browser-mocks title: "@kbn/core-security-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-browser-mocks plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-browser-mocks'] --- import kbnCoreSecurityBrowserMocksObj from './kbn_core_security_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_security_common.mdx b/api_docs/kbn_core_security_common.mdx index 550c7ee2d174c..784b57ee9703f 100644 --- a/api_docs/kbn_core_security_common.mdx +++ b/api_docs/kbn_core_security_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-common title: "@kbn/core-security-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-common plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-common'] --- import kbnCoreSecurityCommonObj from './kbn_core_security_common.devdocs.json'; diff --git a/api_docs/kbn_core_security_server.mdx b/api_docs/kbn_core_security_server.mdx index a47d088d95071..d0e080e22ff5d 100644 --- a/api_docs/kbn_core_security_server.mdx +++ b/api_docs/kbn_core_security_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-server title: "@kbn/core-security-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-server plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-server'] --- import kbnCoreSecurityServerObj from './kbn_core_security_server.devdocs.json'; diff --git a/api_docs/kbn_core_security_server_internal.mdx b/api_docs/kbn_core_security_server_internal.mdx index 976685c74fe2f..699fa40e70673 100644 --- a/api_docs/kbn_core_security_server_internal.mdx +++ b/api_docs/kbn_core_security_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-server-internal title: "@kbn/core-security-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-server-internal plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-server-internal'] --- import kbnCoreSecurityServerInternalObj from './kbn_core_security_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_security_server_mocks.mdx b/api_docs/kbn_core_security_server_mocks.mdx index 8f89a2db2c49d..81cb5989b1a83 100644 --- a/api_docs/kbn_core_security_server_mocks.mdx +++ b/api_docs/kbn_core_security_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-server-mocks title: "@kbn/core-security-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-server-mocks'] --- import kbnCoreSecurityServerMocksObj from './kbn_core_security_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_status_common.mdx b/api_docs/kbn_core_status_common.mdx index 213ad98bd2e3c..cbe81ddc53c79 100644 --- a/api_docs/kbn_core_status_common.mdx +++ b/api_docs/kbn_core_status_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common title: "@kbn/core-status-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common plugin -date: 2024-05-16 +date: 2024-05-20 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 37ff44574b2dd..a6ab0cdded4ae 100644 --- a/api_docs/kbn_core_status_common_internal.mdx +++ b/api_docs/kbn_core_status_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common-internal title: "@kbn/core-status-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 91f9295cbc5be..0b6f7c0cb560d 100644 --- a/api_docs/kbn_core_status_server.mdx +++ b/api_docs/kbn_core_status_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server title: "@kbn/core-status-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server plugin -date: 2024-05-16 +date: 2024-05-20 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 e54304728bb19..6727001ea7d80 100644 --- a/api_docs/kbn_core_status_server_internal.mdx +++ b/api_docs/kbn_core_status_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-internal title: "@kbn/core-status-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 7c5cab677b63d..557c4a6e00dd4 100644 --- a/api_docs/kbn_core_status_server_mocks.mdx +++ b/api_docs/kbn_core_status_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-mocks title: "@kbn/core-status-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 2f4139687a853..660b3da203adc 100644 --- a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx +++ b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-deprecations-getters title: "@kbn/core-test-helpers-deprecations-getters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-deprecations-getters plugin -date: 2024-05-16 +date: 2024-05-20 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 ebac7a0d19a17..3e666591c8547 100644 --- a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx +++ b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-http-setup-browser title: "@kbn/core-test-helpers-http-setup-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-http-setup-browser plugin -date: 2024-05-16 +date: 2024-05-20 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.devdocs.json b/api_docs/kbn_core_test_helpers_kbn_server.devdocs.json index 6631202ad7012..6bf842de7c71a 100644 --- a/api_docs/kbn_core_test_helpers_kbn_server.devdocs.json +++ b/api_docs/kbn_core_test_helpers_kbn_server.devdocs.json @@ -434,7 +434,8 @@ "text": "HttpMethod" }, ", path: string) => ", - "Test" + "SuperTestStatic", + ".Test" ], "path": "packages/core/test-helpers/core-test-helpers-kbn-server/src/create_root.ts", "deprecated": false, @@ -833,7 +834,8 @@ "text": "Root" }, ", path: string) => ", - "Test" + "SuperTestStatic", + ".Test" ], "path": "packages/core/test-helpers/core-test-helpers-kbn-server/src/create_root.ts", "deprecated": false, @@ -895,7 +897,8 @@ "text": "Root" }, ", path: string) => ", - "Test" + "SuperTestStatic", + ".Test" ], "path": "packages/core/test-helpers/core-test-helpers-kbn-server/src/create_root.ts", "deprecated": false, @@ -957,7 +960,8 @@ "text": "Root" }, ", path: string) => ", - "Test" + "SuperTestStatic", + ".Test" ], "path": "packages/core/test-helpers/core-test-helpers-kbn-server/src/create_root.ts", "deprecated": false, @@ -1019,7 +1023,8 @@ "text": "Root" }, ", path: string) => ", - "Test" + "SuperTestStatic", + ".Test" ], "path": "packages/core/test-helpers/core-test-helpers-kbn-server/src/create_root.ts", "deprecated": false, @@ -1081,7 +1086,8 @@ "text": "Root" }, ", path: string) => ", - "Test" + "SuperTestStatic", + ".Test" ], "path": "packages/core/test-helpers/core-test-helpers-kbn-server/src/create_root.ts", "deprecated": false, @@ -1143,7 +1149,8 @@ "text": "Root" }, ", path: string) => ", - "Test" + "SuperTestStatic", + ".Test" ], "path": "packages/core/test-helpers/core-test-helpers-kbn-server/src/create_root.ts", "deprecated": false, diff --git a/api_docs/kbn_core_test_helpers_kbn_server.mdx b/api_docs/kbn_core_test_helpers_kbn_server.mdx index 3cb0613ddcc29..8c77a30d6bbca 100644 --- a/api_docs/kbn_core_test_helpers_kbn_server.mdx +++ b/api_docs/kbn_core_test_helpers_kbn_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-kbn-server title: "@kbn/core-test-helpers-kbn-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-kbn-server plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-kbn-server'] --- import kbnCoreTestHelpersKbnServerObj from './kbn_core_test_helpers_kbn_server.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_model_versions.mdx b/api_docs/kbn_core_test_helpers_model_versions.mdx index c621428bc5c16..4ba5a9bde15ba 100644 --- a/api_docs/kbn_core_test_helpers_model_versions.mdx +++ b/api_docs/kbn_core_test_helpers_model_versions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-model-versions title: "@kbn/core-test-helpers-model-versions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-model-versions plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-model-versions'] --- import kbnCoreTestHelpersModelVersionsObj from './kbn_core_test_helpers_model_versions.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx index 41717d02ebda0..60f194cd061db 100644 --- a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx +++ b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-so-type-serializer title: "@kbn/core-test-helpers-so-type-serializer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-so-type-serializer plugin -date: 2024-05-16 +date: 2024-05-20 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 5dbd0fe47d419..36bc1e3ebba5f 100644 --- a/api_docs/kbn_core_test_helpers_test_utils.mdx +++ b/api_docs/kbn_core_test_helpers_test_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-test-utils title: "@kbn/core-test-helpers-test-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-test-utils plugin -date: 2024-05-16 +date: 2024-05-20 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 69a121f9d8727..0655750340378 100644 --- a/api_docs/kbn_core_theme_browser.mdx +++ b/api_docs/kbn_core_theme_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser title: "@kbn/core-theme-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser'] --- import kbnCoreThemeBrowserObj from './kbn_core_theme_browser.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_mocks.mdx b/api_docs/kbn_core_theme_browser_mocks.mdx index bb7e1cb54c324..3a597038edc1c 100644 --- a/api_docs/kbn_core_theme_browser_mocks.mdx +++ b/api_docs/kbn_core_theme_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-mocks title: "@kbn/core-theme-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 47c96e90f1a56..4a3ef71a5ee38 100644 --- a/api_docs/kbn_core_ui_settings_browser.mdx +++ b/api_docs/kbn_core_ui_settings_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser title: "@kbn/core-ui-settings-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser plugin -date: 2024-05-16 +date: 2024-05-20 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 916abc90e50aa..03c8cea813f47 100644 --- a/api_docs/kbn_core_ui_settings_browser_internal.mdx +++ b/api_docs/kbn_core_ui_settings_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-internal title: "@kbn/core-ui-settings-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 babdcecccca13..7681ee0ad948e 100644 --- a/api_docs/kbn_core_ui_settings_browser_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-mocks title: "@kbn/core-ui-settings-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 9a91bc8af2832..c92582e578de8 100644 --- a/api_docs/kbn_core_ui_settings_common.mdx +++ b/api_docs/kbn_core_ui_settings_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-common title: "@kbn/core-ui-settings-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-common plugin -date: 2024-05-16 +date: 2024-05-20 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 33ac9cd874775..878d3a8d57db0 100644 --- a/api_docs/kbn_core_ui_settings_server.mdx +++ b/api_docs/kbn_core_ui_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server title: "@kbn/core-ui-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server plugin -date: 2024-05-16 +date: 2024-05-20 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 14c86f96ad64e..4cf558fca1530 100644 --- a/api_docs/kbn_core_ui_settings_server_internal.mdx +++ b/api_docs/kbn_core_ui_settings_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-internal title: "@kbn/core-ui-settings-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 509bf5ffbd485..e5f208c7e76b1 100644 --- a/api_docs/kbn_core_ui_settings_server_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-mocks title: "@kbn/core-ui-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 c64a7e09fb80d..a70b2f2ef2e5d 100644 --- a/api_docs/kbn_core_usage_data_server.mdx +++ b/api_docs/kbn_core_usage_data_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server title: "@kbn/core-usage-data-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server plugin -date: 2024-05-16 +date: 2024-05-20 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 26122235e86af..6c242087b79ee 100644 --- a/api_docs/kbn_core_usage_data_server_internal.mdx +++ b/api_docs/kbn_core_usage_data_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-internal title: "@kbn/core-usage-data-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-internal plugin -date: 2024-05-16 +date: 2024-05-20 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 9d09b1b4c8d11..da199ae391538 100644 --- a/api_docs/kbn_core_usage_data_server_mocks.mdx +++ b/api_docs/kbn_core_usage_data_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-mocks title: "@kbn/core-usage-data-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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_profile_browser.mdx b/api_docs/kbn_core_user_profile_browser.mdx index 3a423d1392e2e..f6a183e6ed410 100644 --- a/api_docs/kbn_core_user_profile_browser.mdx +++ b/api_docs/kbn_core_user_profile_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-browser title: "@kbn/core-user-profile-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-browser plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-browser'] --- import kbnCoreUserProfileBrowserObj from './kbn_core_user_profile_browser.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_browser_internal.mdx b/api_docs/kbn_core_user_profile_browser_internal.mdx index 2cacf48a952b6..081ba81ed87c2 100644 --- a/api_docs/kbn_core_user_profile_browser_internal.mdx +++ b/api_docs/kbn_core_user_profile_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-browser-internal title: "@kbn/core-user-profile-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-browser-internal plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-browser-internal'] --- import kbnCoreUserProfileBrowserInternalObj from './kbn_core_user_profile_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_browser_mocks.mdx b/api_docs/kbn_core_user_profile_browser_mocks.mdx index efcab025912a0..d501ff9d9abfa 100644 --- a/api_docs/kbn_core_user_profile_browser_mocks.mdx +++ b/api_docs/kbn_core_user_profile_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-browser-mocks title: "@kbn/core-user-profile-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-browser-mocks plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-browser-mocks'] --- import kbnCoreUserProfileBrowserMocksObj from './kbn_core_user_profile_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_common.mdx b/api_docs/kbn_core_user_profile_common.mdx index 704eb2eca7cad..51a6773703431 100644 --- a/api_docs/kbn_core_user_profile_common.mdx +++ b/api_docs/kbn_core_user_profile_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-common title: "@kbn/core-user-profile-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-common plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-common'] --- import kbnCoreUserProfileCommonObj from './kbn_core_user_profile_common.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_server.mdx b/api_docs/kbn_core_user_profile_server.mdx index 1051ea1056ab4..62077a80f00a4 100644 --- a/api_docs/kbn_core_user_profile_server.mdx +++ b/api_docs/kbn_core_user_profile_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-server title: "@kbn/core-user-profile-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-server plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-server'] --- import kbnCoreUserProfileServerObj from './kbn_core_user_profile_server.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_server_internal.mdx b/api_docs/kbn_core_user_profile_server_internal.mdx index 06aef9bfae569..c2f3a4d5bba48 100644 --- a/api_docs/kbn_core_user_profile_server_internal.mdx +++ b/api_docs/kbn_core_user_profile_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-server-internal title: "@kbn/core-user-profile-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-server-internal plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-server-internal'] --- import kbnCoreUserProfileServerInternalObj from './kbn_core_user_profile_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_server_mocks.mdx b/api_docs/kbn_core_user_profile_server_mocks.mdx index 35a8a1dad00ec..24df1b2fc824e 100644 --- a/api_docs/kbn_core_user_profile_server_mocks.mdx +++ b/api_docs/kbn_core_user_profile_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-server-mocks title: "@kbn/core-user-profile-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-server-mocks'] --- import kbnCoreUserProfileServerMocksObj from './kbn_core_user_profile_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 41c5300faf2f3..93aec5bc15e44 100644 --- a/api_docs/kbn_core_user_settings_server.mdx +++ b/api_docs/kbn_core_user_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server title: "@kbn/core-user-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server plugin -date: 2024-05-16 +date: 2024-05-20 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_mocks.mdx b/api_docs/kbn_core_user_settings_server_mocks.mdx index ee8688883f053..beeb99f06dba5 100644 --- a/api_docs/kbn_core_user_settings_server_mocks.mdx +++ b/api_docs/kbn_core_user_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server-mocks title: "@kbn/core-user-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 5949bd5960c1d..e48726adf6cb9 100644 --- a/api_docs/kbn_crypto.mdx +++ b/api_docs/kbn_crypto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto title: "@kbn/crypto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto plugin -date: 2024-05-16 +date: 2024-05-20 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 a4ecdcad0cc3d..a63c76df61ca3 100644 --- a/api_docs/kbn_crypto_browser.mdx +++ b/api_docs/kbn_crypto_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto-browser title: "@kbn/crypto-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto-browser plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto-browser'] --- import kbnCryptoBrowserObj from './kbn_crypto_browser.devdocs.json'; diff --git a/api_docs/kbn_custom_icons.mdx b/api_docs/kbn_custom_icons.mdx index f7fb94ce846fa..8f60f2d77a286 100644 --- a/api_docs/kbn_custom_icons.mdx +++ b/api_docs/kbn_custom_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-custom-icons title: "@kbn/custom-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/custom-icons plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/custom-icons'] --- import kbnCustomIconsObj from './kbn_custom_icons.devdocs.json'; diff --git a/api_docs/kbn_custom_integrations.mdx b/api_docs/kbn_custom_integrations.mdx index 20780b9f3e6eb..cd8b527a77f95 100644 --- a/api_docs/kbn_custom_integrations.mdx +++ b/api_docs/kbn_custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-custom-integrations title: "@kbn/custom-integrations" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/custom-integrations plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/custom-integrations'] --- import kbnCustomIntegrationsObj from './kbn_custom_integrations.devdocs.json'; diff --git a/api_docs/kbn_cypress_config.mdx b/api_docs/kbn_cypress_config.mdx index cfec3b57d7313..18e59134e133e 100644 --- a/api_docs/kbn_cypress_config.mdx +++ b/api_docs/kbn_cypress_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cypress-config title: "@kbn/cypress-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cypress-config plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cypress-config'] --- import kbnCypressConfigObj from './kbn_cypress_config.devdocs.json'; diff --git a/api_docs/kbn_data_forge.mdx b/api_docs/kbn_data_forge.mdx index c907243a61786..77b427641fd06 100644 --- a/api_docs/kbn_data_forge.mdx +++ b/api_docs/kbn_data_forge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-forge title: "@kbn/data-forge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-forge plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-forge'] --- import kbnDataForgeObj from './kbn_data_forge.devdocs.json'; diff --git a/api_docs/kbn_data_service.mdx b/api_docs/kbn_data_service.mdx index b82222aaa03a6..f7c8cd4f79e50 100644 --- a/api_docs/kbn_data_service.mdx +++ b/api_docs/kbn_data_service.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-service title: "@kbn/data-service" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-service plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-service'] --- import kbnDataServiceObj from './kbn_data_service.devdocs.json'; diff --git a/api_docs/kbn_data_stream_adapter.mdx b/api_docs/kbn_data_stream_adapter.mdx index 8b5df9f42372e..b3122f7ae51bd 100644 --- a/api_docs/kbn_data_stream_adapter.mdx +++ b/api_docs/kbn_data_stream_adapter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-stream-adapter title: "@kbn/data-stream-adapter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-stream-adapter plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-stream-adapter'] --- import kbnDataStreamAdapterObj from './kbn_data_stream_adapter.devdocs.json'; diff --git a/api_docs/kbn_data_view_utils.mdx b/api_docs/kbn_data_view_utils.mdx index 50cfefb4d0524..aa2394b3a95fd 100644 --- a/api_docs/kbn_data_view_utils.mdx +++ b/api_docs/kbn_data_view_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-view-utils title: "@kbn/data-view-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-view-utils plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-view-utils'] --- import kbnDataViewUtilsObj from './kbn_data_view_utils.devdocs.json'; diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index 7f6779a772c18..1d246c0a39d21 100644 --- a/api_docs/kbn_datemath.mdx +++ b/api_docs/kbn_datemath.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-datemath title: "@kbn/datemath" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/datemath plugin -date: 2024-05-16 +date: 2024-05-20 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 13c43496b9e5d..c07271541acd8 100644 --- a/api_docs/kbn_deeplinks_analytics.mdx +++ b/api_docs/kbn_deeplinks_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-analytics title: "@kbn/deeplinks-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-analytics plugin -date: 2024-05-16 +date: 2024-05-20 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 b600b5be8ad2e..6adb69716e382 100644 --- a/api_docs/kbn_deeplinks_devtools.mdx +++ b/api_docs/kbn_deeplinks_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-devtools title: "@kbn/deeplinks-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-devtools plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-devtools'] --- import kbnDeeplinksDevtoolsObj from './kbn_deeplinks_devtools.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_fleet.mdx b/api_docs/kbn_deeplinks_fleet.mdx index 0b4123bbc1a7d..4fc9389ec9b65 100644 --- a/api_docs/kbn_deeplinks_fleet.mdx +++ b/api_docs/kbn_deeplinks_fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-fleet title: "@kbn/deeplinks-fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-fleet plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-fleet'] --- import kbnDeeplinksFleetObj from './kbn_deeplinks_fleet.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_management.mdx b/api_docs/kbn_deeplinks_management.mdx index 8e16d449af436..7b777f89de6b1 100644 --- a/api_docs/kbn_deeplinks_management.mdx +++ b/api_docs/kbn_deeplinks_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-management title: "@kbn/deeplinks-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-management plugin -date: 2024-05-16 +date: 2024-05-20 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 55f8ba8822713..ed60480d3d0fe 100644 --- a/api_docs/kbn_deeplinks_ml.mdx +++ b/api_docs/kbn_deeplinks_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-ml title: "@kbn/deeplinks-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-ml plugin -date: 2024-05-16 +date: 2024-05-20 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 e91c7b8196936..8d3ff8af26804 100644 --- a/api_docs/kbn_deeplinks_observability.mdx +++ b/api_docs/kbn_deeplinks_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-observability title: "@kbn/deeplinks-observability" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-observability plugin -date: 2024-05-16 +date: 2024-05-20 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 fcbb77e852a40..aa1a17f089365 100644 --- a/api_docs/kbn_deeplinks_search.mdx +++ b/api_docs/kbn_deeplinks_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-search title: "@kbn/deeplinks-search" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-search plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-search'] --- import kbnDeeplinksSearchObj from './kbn_deeplinks_search.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_security.mdx b/api_docs/kbn_deeplinks_security.mdx index af8be0fa8f5a1..187a9890bda0d 100644 --- a/api_docs/kbn_deeplinks_security.mdx +++ b/api_docs/kbn_deeplinks_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-security title: "@kbn/deeplinks-security" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-security plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-security'] --- import kbnDeeplinksSecurityObj from './kbn_deeplinks_security.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_shared.mdx b/api_docs/kbn_deeplinks_shared.mdx index c47b419b735be..a4b5c02061939 100644 --- a/api_docs/kbn_deeplinks_shared.mdx +++ b/api_docs/kbn_deeplinks_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-shared title: "@kbn/deeplinks-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-shared plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-shared'] --- import kbnDeeplinksSharedObj from './kbn_deeplinks_shared.devdocs.json'; diff --git a/api_docs/kbn_default_nav_analytics.mdx b/api_docs/kbn_default_nav_analytics.mdx index 4f7f43dd2a8da..03124f5a248c1 100644 --- a/api_docs/kbn_default_nav_analytics.mdx +++ b/api_docs/kbn_default_nav_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-analytics title: "@kbn/default-nav-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-analytics plugin -date: 2024-05-16 +date: 2024-05-20 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 75d4725ef357b..36894a36fd468 100644 --- a/api_docs/kbn_default_nav_devtools.mdx +++ b/api_docs/kbn_default_nav_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-devtools title: "@kbn/default-nav-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-devtools plugin -date: 2024-05-16 +date: 2024-05-20 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 363b55c0d16a2..7dd0d287bacf4 100644 --- a/api_docs/kbn_default_nav_management.mdx +++ b/api_docs/kbn_default_nav_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-management title: "@kbn/default-nav-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-management plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-management'] --- import kbnDefaultNavManagementObj from './kbn_default_nav_management.devdocs.json'; diff --git a/api_docs/kbn_default_nav_ml.mdx b/api_docs/kbn_default_nav_ml.mdx index a3fdf8e052d2f..ce652d54e07ab 100644 --- a/api_docs/kbn_default_nav_ml.mdx +++ b/api_docs/kbn_default_nav_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-ml title: "@kbn/default-nav-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-ml plugin -date: 2024-05-16 +date: 2024-05-20 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 9e625d94b1890..c46e74bd04532 100644 --- a/api_docs/kbn_dev_cli_errors.mdx +++ b/api_docs/kbn_dev_cli_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-errors title: "@kbn/dev-cli-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-errors plugin -date: 2024-05-16 +date: 2024-05-20 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 e71eef2a1bc8c..6918c4038640c 100644 --- a/api_docs/kbn_dev_cli_runner.mdx +++ b/api_docs/kbn_dev_cli_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-runner title: "@kbn/dev-cli-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-runner plugin -date: 2024-05-16 +date: 2024-05-20 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 d3b9373be0271..9868b51309251 100644 --- a/api_docs/kbn_dev_proc_runner.mdx +++ b/api_docs/kbn_dev_proc_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-proc-runner title: "@kbn/dev-proc-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-proc-runner plugin -date: 2024-05-16 +date: 2024-05-20 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 db6b87a2dd9bc..772a9c2e99188 100644 --- a/api_docs/kbn_dev_utils.mdx +++ b/api_docs/kbn_dev_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-utils title: "@kbn/dev-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-utils plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_discover_utils.devdocs.json b/api_docs/kbn_discover_utils.devdocs.json index dd1194aef57ea..88dc4dd281120 100644 --- a/api_docs/kbn_discover_utils.devdocs.json +++ b/api_docs/kbn_discover_utils.devdocs.json @@ -1011,7 +1011,7 @@ "label": "isLegacyTableEnabled", "description": [], "signature": [ - "({\n uiSettings,\n isTextBasedQueryMode,\n}: { uiSettings: ", + "({\n uiSettings,\n isEsqlMode,\n}: { uiSettings: ", { "pluginId": "@kbn/core-ui-settings-browser", "scope": "common", @@ -1019,7 +1019,7 @@ "section": "def-common.IUiSettingsClient", "text": "IUiSettingsClient" }, - "; isTextBasedQueryMode: boolean; }) => boolean" + "; isEsqlMode: boolean; }) => boolean" ], "path": "packages/kbn-discover-utils/src/utils/is_legacy_table_enabled.ts", "deprecated": false, @@ -1030,7 +1030,7 @@ "id": "def-common.isLegacyTableEnabled.$1", "type": "Object", "tags": [], - "label": "{\n uiSettings,\n isTextBasedQueryMode,\n}", + "label": "{\n uiSettings,\n isEsqlMode,\n}", "description": [], "path": "packages/kbn-discover-utils/src/utils/is_legacy_table_enabled.ts", "deprecated": false, @@ -1058,10 +1058,10 @@ }, { "parentPluginId": "@kbn/discover-utils", - "id": "def-common.isLegacyTableEnabled.$1.isTextBasedQueryMode", + "id": "def-common.isLegacyTableEnabled.$1.isEsqlMode", "type": "boolean", "tags": [], - "label": "isTextBasedQueryMode", + "label": "isEsqlMode", "description": [], "path": "packages/kbn-discover-utils/src/utils/is_legacy_table_enabled.ts", "deprecated": false, diff --git a/api_docs/kbn_discover_utils.mdx b/api_docs/kbn_discover_utils.mdx index baef7cbcccd26..2bb02673cbaec 100644 --- a/api_docs/kbn_discover_utils.mdx +++ b/api_docs/kbn_discover_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-discover-utils title: "@kbn/discover-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/discover-utils plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/discover-utils'] --- import kbnDiscoverUtilsObj from './kbn_discover_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.devdocs.json b/api_docs/kbn_doc_links.devdocs.json index 947b9db8fccbe..db61b6fa68e9e 100644 --- a/api_docs/kbn_doc_links.devdocs.json +++ b/api_docs/kbn_doc_links.devdocs.json @@ -546,7 +546,7 @@ "label": "securitySolution", "description": [], "signature": [ - "{ readonly artifactControl: string; readonly trustedApps: string; readonly eventFilters: string; readonly blocklist: string; readonly endpointArtifacts: string; readonly policyResponseTroubleshooting: { full_disk_access: string; macos_system_ext: string; linux_deadlock: string; }; readonly packageActionTroubleshooting: { es_connection: string; }; readonly threatIntelInt: string; readonly responseActions: string; readonly configureEndpointIntegrationPolicy: string; readonly exceptions: { value_lists: string; }; readonly privileges: string; readonly manageDetectionRules: string; readonly createEsqlRuleType: string; readonly ruleUiAdvancedParams: string; readonly entityAnalytics: { readonly riskScorePrerequisites: string; readonly hostRiskScore: string; readonly userRiskScore: string; readonly entityRiskScoring: string; readonly assetCriticality: string; }; readonly detectionEngineOverview: string; }" + "{ readonly artifactControl: string; readonly trustedApps: string; readonly eventFilters: string; readonly blocklist: string; readonly endpointArtifacts: string; readonly policyResponseTroubleshooting: { full_disk_access: string; macos_system_ext: string; linux_deadlock: string; }; readonly packageActionTroubleshooting: { es_connection: string; }; readonly threatIntelInt: string; readonly responseActions: string; readonly configureEndpointIntegrationPolicy: string; readonly exceptions: { value_lists: string; }; readonly privileges: string; readonly manageDetectionRules: string; readonly createDetectionRules: string; readonly createEsqlRuleType: string; readonly ruleUiAdvancedParams: string; readonly entityAnalytics: { readonly riskScorePrerequisites: string; readonly hostRiskScore: string; readonly userRiskScore: string; readonly entityRiskScoring: string; readonly assetCriticality: string; }; readonly detectionEngineOverview: string; }" ], "path": "packages/kbn-doc-links/src/types.ts", "deprecated": false, diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index bf85dc5df4955..cab07f28209d1 100644 --- a/api_docs/kbn_doc_links.mdx +++ b/api_docs/kbn_doc_links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-doc-links title: "@kbn/doc-links" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/doc-links plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/doc-links'] --- import kbnDocLinksObj from './kbn_doc_links.devdocs.json'; diff --git a/api_docs/kbn_docs_utils.mdx b/api_docs/kbn_docs_utils.mdx index 415b2a02b41b8..0358613769c72 100644 --- a/api_docs/kbn_docs_utils.mdx +++ b/api_docs/kbn_docs_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-docs-utils title: "@kbn/docs-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/docs-utils plugin -date: 2024-05-16 +date: 2024-05-20 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 402bfbb3f90c4..64ffd0d644200 100644 --- a/api_docs/kbn_dom_drag_drop.mdx +++ b/api_docs/kbn_dom_drag_drop.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dom-drag-drop title: "@kbn/dom-drag-drop" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dom-drag-drop plugin -date: 2024-05-16 +date: 2024-05-20 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 dab4c21a23621..b9973c05fa579 100644 --- a/api_docs/kbn_ebt_tools.mdx +++ b/api_docs/kbn_ebt_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ebt-tools title: "@kbn/ebt-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ebt-tools plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] --- import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; diff --git a/api_docs/kbn_ecs_data_quality_dashboard.mdx b/api_docs/kbn_ecs_data_quality_dashboard.mdx index dbf8449d62373..a3030a1edd466 100644 --- a/api_docs/kbn_ecs_data_quality_dashboard.mdx +++ b/api_docs/kbn_ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs-data-quality-dashboard title: "@kbn/ecs-data-quality-dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs-data-quality-dashboard plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs-data-quality-dashboard'] --- import kbnEcsDataQualityDashboardObj from './kbn_ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/kbn_elastic_agent_utils.mdx b/api_docs/kbn_elastic_agent_utils.mdx index 06898632b335c..962f4257ed9c1 100644 --- a/api_docs/kbn_elastic_agent_utils.mdx +++ b/api_docs/kbn_elastic_agent_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-agent-utils title: "@kbn/elastic-agent-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-agent-utils plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-agent-utils'] --- import kbnElasticAgentUtilsObj from './kbn_elastic_agent_utils.devdocs.json'; diff --git a/api_docs/kbn_elastic_assistant.devdocs.json b/api_docs/kbn_elastic_assistant.devdocs.json index 19c63390e9bb5..15d2789e99175 100644 --- a/api_docs/kbn_elastic_assistant.devdocs.json +++ b/api_docs/kbn_elastic_assistant.devdocs.json @@ -707,7 +707,7 @@ "\n`useAssistantOverlay` is a hook that registers context with the assistant overlay, and\nreturns an optional `showAssistantOverlay` function to display the assistant overlay.\nAs an alterative to using the `showAssistantOverlay` returned from this hook, you may\nuse the `NewChatByTitle` component and pass it the `promptContextId` returned by this hook.\n\nUSE THIS WHEN: You want to register context in one part of the tree, and then show\na _New chat_ button in another part of the tree without passing around the data, or when\nyou want to build a custom `New chat` button with features not not provided by the\n`NewChat` component." ], "signature": [ - "(category: string, conversationTitle: string | null, description: string, getPromptContext: () => Promise | Promise>, id: string | null, suggestedUserPrompt: string | null | undefined, tooltip: React.ReactNode, replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | null | undefined) => UseAssistantOverlay" + "(category: string, conversationTitle: string | null, description: string, getPromptContext: () => Promise | Promise>, id: string | null, suggestedUserPrompt: string | null | undefined, tooltip: React.ReactNode, isAssistantEnabled: boolean, replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | null | undefined) => UseAssistantOverlay" ], "path": "x-pack/packages/kbn-elastic-assistant/impl/assistant/use_assistant_overlay/index.tsx", "deprecated": false, @@ -821,6 +821,21 @@ { "parentPluginId": "@kbn/elastic-assistant", "id": "def-public.useAssistantOverlay.$8", + "type": "boolean", + "tags": [], + "label": "isAssistantEnabled", + "description": [], + "signature": [ + "boolean" + ], + "path": "x-pack/packages/kbn-elastic-assistant/impl/assistant/use_assistant_overlay/index.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/elastic-assistant", + "id": "def-public.useAssistantOverlay.$9", "type": "CompoundType", "tags": [], "label": "replacements", diff --git a/api_docs/kbn_elastic_assistant.mdx b/api_docs/kbn_elastic_assistant.mdx index 502e690f46bb0..0bc60bbc90ce4 100644 --- a/api_docs/kbn_elastic_assistant.mdx +++ b/api_docs/kbn_elastic_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-assistant title: "@kbn/elastic-assistant" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-assistant plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-assistant'] --- import kbnElasticAssistantObj from './kbn_elastic_assistant.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/security-generative-ai](https://github.com/orgs/elastic/teams/ | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 173 | 0 | 146 | 9 | +| 174 | 0 | 147 | 9 | ## Client diff --git a/api_docs/kbn_elastic_assistant_common.mdx b/api_docs/kbn_elastic_assistant_common.mdx index 285fd33c75287..1d9c881ebc379 100644 --- a/api_docs/kbn_elastic_assistant_common.mdx +++ b/api_docs/kbn_elastic_assistant_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-assistant-common title: "@kbn/elastic-assistant-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-assistant-common plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-assistant-common'] --- import kbnElasticAssistantCommonObj from './kbn_elastic_assistant_common.devdocs.json'; diff --git a/api_docs/kbn_entities_schema.devdocs.json b/api_docs/kbn_entities_schema.devdocs.json new file mode 100644 index 0000000000000..e393324139b95 --- /dev/null +++ b/api_docs/kbn_entities_schema.devdocs.json @@ -0,0 +1,525 @@ +{ + "id": "@kbn/entities-schema", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [ + { + "parentPluginId": "@kbn/entities-schema", + "id": "def-common.BasicAggregations", + "type": "Enum", + "tags": [], + "label": "BasicAggregations", + "description": [], + "path": "x-pack/packages/kbn-entities-schema/src/schema/common.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/entities-schema", + "id": "def-common.EntityType", + "type": "Enum", + "tags": [], + "label": "EntityType", + "description": [], + "path": "x-pack/packages/kbn-entities-schema/src/schema/common.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "misc": [ + { + "parentPluginId": "@kbn/entities-schema", + "id": "def-common.EntityDefinition", + "type": "Type", + "tags": [], + "label": "EntityDefinition", + "description": [], + "signature": [ + "{ id: string; type: ", + { + "pluginId": "@kbn/entities-schema", + "scope": "common", + "docId": "kibKbnEntitiesSchemaPluginApi", + "section": "def-common.EntityType", + "text": "EntityType" + }, + "; name: string; indexPatterns: string[]; timestampField: string; identityFields: ({ field: string; optional: boolean; } | { field: string; optional: boolean; })[]; identityTemplate: string; lookback: { toJSON: () => string; clone(): moment.Duration; humanize(argWithSuffix?: boolean | undefined, argThresholds?: moment.argThresholdOpts | undefined): string; humanize(argThresholds?: moment.argThresholdOpts | undefined): string; abs(): moment.Duration; as(units: moment.unitOfTime.Base): number; get(units: moment.unitOfTime.Base): number; milliseconds(): number; asMilliseconds(): number; seconds(): number; asSeconds(): number; minutes(): number; asMinutes(): number; hours(): number; asHours(): number; days(): number; asDays(): number; weeks(): number; asWeeks(): number; months(): number; asMonths(): number; years(): number; asYears(): number; add(inp?: moment.DurationInputArg1, unit?: moment.unitOfTime.DurationConstructor | undefined): moment.Duration; subtract(inp?: moment.DurationInputArg1, unit?: moment.unitOfTime.DurationConstructor | undefined): moment.Duration; locale(): string; locale(locale: moment.LocaleSpecifier): moment.Duration; localeData(): moment.Locale; toISOString(): string; isValid(): boolean; lang(locale: moment.LocaleSpecifier): moment.Moment; lang(): moment.Locale; toIsoString(): string; format: moment.Format; }; description?: string | undefined; filter?: string | undefined; metadata?: ({ source: string; destination?: string | undefined; limit?: number | undefined; } | { source: string; destination: string; limit: number; })[] | undefined; metrics?: { name: string; metrics: ({ name: string; field: string; aggregation: ", + { + "pluginId": "@kbn/entities-schema", + "scope": "common", + "docId": "kibKbnEntitiesSchemaPluginApi", + "section": "def-common.BasicAggregations", + "text": "BasicAggregations" + }, + "; filter?: string | undefined; } | { name: string; aggregation: \"doc_count\"; filter?: string | undefined; } | { name: string; field: string; percentile: number; aggregation: \"percentile\"; filter?: string | undefined; })[]; equation: string; }[] | undefined; staticFields?: Record | undefined; settings?: { syncField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; } | undefined; }" + ], + "path": "x-pack/packages/kbn-entities-schema/src/schema/entity_definition.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/entities-schema", + "id": "def-common.KeyMetric", + "type": "Type", + "tags": [], + "label": "KeyMetric", + "description": [], + "signature": [ + "{ name: string; metrics: ({ name: string; field: string; aggregation: ", + { + "pluginId": "@kbn/entities-schema", + "scope": "common", + "docId": "kibKbnEntitiesSchemaPluginApi", + "section": "def-common.BasicAggregations", + "text": "BasicAggregations" + }, + "; filter?: string | undefined; } | { name: string; aggregation: \"doc_count\"; filter?: string | undefined; } | { name: string; field: string; percentile: number; aggregation: \"percentile\"; filter?: string | undefined; })[]; equation: string; }" + ], + "path": "x-pack/packages/kbn-entities-schema/src/schema/common.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/entities-schema", + "id": "def-common.Metric", + "type": "Type", + "tags": [], + "label": "Metric", + "description": [], + "signature": [ + "{ name: string; field: string; aggregation: ", + { + "pluginId": "@kbn/entities-schema", + "scope": "common", + "docId": "kibKbnEntitiesSchemaPluginApi", + "section": "def-common.BasicAggregations", + "text": "BasicAggregations" + }, + "; filter?: string | undefined; } | { name: string; aggregation: \"doc_count\"; filter?: string | undefined; } | { name: string; field: string; percentile: number; aggregation: \"percentile\"; filter?: string | undefined; }" + ], + "path": "x-pack/packages/kbn-entities-schema/src/schema/common.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [ + { + "parentPluginId": "@kbn/entities-schema", + "id": "def-common.arrayOfStringsSchema", + "type": "Object", + "tags": [], + "label": "arrayOfStringsSchema", + "description": [], + "signature": [ + "Zod.ZodArray" + ], + "path": "x-pack/packages/kbn-entities-schema/src/schema/common.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/entities-schema", + "id": "def-common.basicAggregationsSchema", + "type": "Object", + "tags": [], + "label": "basicAggregationsSchema", + "description": [], + "signature": [ + "Zod.ZodNativeEnum" + ], + "path": "x-pack/packages/kbn-entities-schema/src/schema/common.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/entities-schema", + "id": "def-common.basicMetricWithFieldSchema", + "type": "Object", + "tags": [], + "label": "basicMetricWithFieldSchema", + "description": [], + "signature": [ + "Zod.ZodObject<{ name: Zod.ZodString; aggregation: Zod.ZodNativeEnum; field: Zod.ZodString; filter: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { name: string; field: string; aggregation: ", + { + "pluginId": "@kbn/entities-schema", + "scope": "common", + "docId": "kibKbnEntitiesSchemaPluginApi", + "section": "def-common.BasicAggregations", + "text": "BasicAggregations" + }, + "; filter?: string | undefined; }, { name: string; field: string; aggregation: ", + { + "pluginId": "@kbn/entities-schema", + "scope": "common", + "docId": "kibKbnEntitiesSchemaPluginApi", + "section": "def-common.BasicAggregations", + "text": "BasicAggregations" + }, + "; filter?: string | undefined; }>" + ], + "path": "x-pack/packages/kbn-entities-schema/src/schema/common.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/entities-schema", + "id": "def-common.docCountMetricSchema", + "type": "Object", + "tags": [], + "label": "docCountMetricSchema", + "description": [], + "signature": [ + "Zod.ZodObject<{ name: Zod.ZodString; aggregation: Zod.ZodLiteral<\"doc_count\">; filter: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { name: string; aggregation: \"doc_count\"; filter?: string | undefined; }, { name: string; aggregation: \"doc_count\"; filter?: string | undefined; }>" + ], + "path": "x-pack/packages/kbn-entities-schema/src/schema/common.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/entities-schema", + "id": "def-common.durationSchema", + "type": "Object", + "tags": [], + "label": "durationSchema", + "description": [], + "signature": [ + "Zod.ZodEffects string; clone(): moment.Duration; humanize(argWithSuffix?: boolean | undefined, argThresholds?: moment.argThresholdOpts | undefined): string; humanize(argThresholds?: moment.argThresholdOpts | undefined): string; abs(): moment.Duration; as(units: moment.unitOfTime.Base): number; get(units: moment.unitOfTime.Base): number; milliseconds(): number; asMilliseconds(): number; seconds(): number; asSeconds(): number; minutes(): number; asMinutes(): number; hours(): number; asHours(): number; days(): number; asDays(): number; weeks(): number; asWeeks(): number; months(): number; asMonths(): number; years(): number; asYears(): number; add(inp?: moment.DurationInputArg1, unit?: moment.unitOfTime.DurationConstructor | undefined): moment.Duration; subtract(inp?: moment.DurationInputArg1, unit?: moment.unitOfTime.DurationConstructor | undefined): moment.Duration; locale(): string; locale(locale: moment.LocaleSpecifier): moment.Duration; localeData(): moment.Locale; toISOString(): string; isValid(): boolean; lang(locale: moment.LocaleSpecifier): moment.Moment; lang(): moment.Locale; toIsoString(): string; format: moment.Format; }, string>" + ], + "path": "x-pack/packages/kbn-entities-schema/src/schema/common.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/entities-schema", + "id": "def-common.entityDefinitionSchema", + "type": "Object", + "tags": [], + "label": "entityDefinitionSchema", + "description": [], + "signature": [ + "Zod.ZodObject<{ id: Zod.ZodString; name: Zod.ZodString; description: Zod.ZodOptional; type: Zod.ZodNativeEnum; filter: Zod.ZodOptional; indexPatterns: Zod.ZodArray; identityFields: Zod.ZodArray, Zod.ZodEffects]>, \"many\">; identityTemplate: Zod.ZodString; metadata: Zod.ZodOptional; limit: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { source: string; destination?: string | undefined; limit?: number | undefined; }, { source: string; destination?: string | undefined; limit?: number | undefined; }>, Zod.ZodEffects]>, \"many\">>; metrics: Zod.ZodOptional; field: Zod.ZodString; filter: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { name: string; field: string; aggregation: ", + { + "pluginId": "@kbn/entities-schema", + "scope": "common", + "docId": "kibKbnEntitiesSchemaPluginApi", + "section": "def-common.BasicAggregations", + "text": "BasicAggregations" + }, + "; filter?: string | undefined; }, { name: string; field: string; aggregation: ", + { + "pluginId": "@kbn/entities-schema", + "scope": "common", + "docId": "kibKbnEntitiesSchemaPluginApi", + "section": "def-common.BasicAggregations", + "text": "BasicAggregations" + }, + "; filter?: string | undefined; }>, Zod.ZodObject<{ name: Zod.ZodString; aggregation: Zod.ZodLiteral<\"doc_count\">; filter: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { name: string; aggregation: \"doc_count\"; filter?: string | undefined; }, { name: string; aggregation: \"doc_count\"; filter?: string | undefined; }>, Zod.ZodObject<{ name: Zod.ZodString; aggregation: Zod.ZodLiteral<\"percentile\">; field: Zod.ZodString; percentile: Zod.ZodNumber; filter: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { name: string; field: string; percentile: number; aggregation: \"percentile\"; filter?: string | undefined; }, { name: string; field: string; percentile: number; aggregation: \"percentile\"; filter?: string | undefined; }>]>, \"many\">; equation: Zod.ZodString; }, \"strip\", Zod.ZodTypeAny, { name: string; metrics: ({ name: string; field: string; aggregation: ", + { + "pluginId": "@kbn/entities-schema", + "scope": "common", + "docId": "kibKbnEntitiesSchemaPluginApi", + "section": "def-common.BasicAggregations", + "text": "BasicAggregations" + }, + "; filter?: string | undefined; } | { name: string; aggregation: \"doc_count\"; filter?: string | undefined; } | { name: string; field: string; percentile: number; aggregation: \"percentile\"; filter?: string | undefined; })[]; equation: string; }, { name: string; metrics: ({ name: string; field: string; aggregation: ", + { + "pluginId": "@kbn/entities-schema", + "scope": "common", + "docId": "kibKbnEntitiesSchemaPluginApi", + "section": "def-common.BasicAggregations", + "text": "BasicAggregations" + }, + "; filter?: string | undefined; } | { name: string; aggregation: \"doc_count\"; filter?: string | undefined; } | { name: string; field: string; percentile: number; aggregation: \"percentile\"; filter?: string | undefined; })[]; equation: string; }>, \"many\">>; staticFields: Zod.ZodOptional>; lookback: Zod.ZodEffects string; clone(): moment.Duration; humanize(argWithSuffix?: boolean | undefined, argThresholds?: moment.argThresholdOpts | undefined): string; humanize(argThresholds?: moment.argThresholdOpts | undefined): string; abs(): moment.Duration; as(units: moment.unitOfTime.Base): number; get(units: moment.unitOfTime.Base): number; milliseconds(): number; asMilliseconds(): number; seconds(): number; asSeconds(): number; minutes(): number; asMinutes(): number; hours(): number; asHours(): number; days(): number; asDays(): number; weeks(): number; asWeeks(): number; months(): number; asMonths(): number; years(): number; asYears(): number; add(inp?: moment.DurationInputArg1, unit?: moment.unitOfTime.DurationConstructor | undefined): moment.Duration; subtract(inp?: moment.DurationInputArg1, unit?: moment.unitOfTime.DurationConstructor | undefined): moment.Duration; locale(): string; locale(locale: moment.LocaleSpecifier): moment.Duration; localeData(): moment.Locale; toISOString(): string; isValid(): boolean; lang(locale: moment.LocaleSpecifier): moment.Moment; lang(): moment.Locale; toIsoString(): string; format: moment.Format; }, string>; timestampField: Zod.ZodString; settings: Zod.ZodOptional; syncDelay: Zod.ZodOptional; frequency: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { syncField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; }, { syncField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { id: string; type: ", + { + "pluginId": "@kbn/entities-schema", + "scope": "common", + "docId": "kibKbnEntitiesSchemaPluginApi", + "section": "def-common.EntityType", + "text": "EntityType" + }, + "; name: string; indexPatterns: string[]; timestampField: string; identityFields: ({ field: string; optional: boolean; } | { field: string; optional: boolean; })[]; identityTemplate: string; lookback: { toJSON: () => string; clone(): moment.Duration; humanize(argWithSuffix?: boolean | undefined, argThresholds?: moment.argThresholdOpts | undefined): string; humanize(argThresholds?: moment.argThresholdOpts | undefined): string; abs(): moment.Duration; as(units: moment.unitOfTime.Base): number; get(units: moment.unitOfTime.Base): number; milliseconds(): number; asMilliseconds(): number; seconds(): number; asSeconds(): number; minutes(): number; asMinutes(): number; hours(): number; asHours(): number; days(): number; asDays(): number; weeks(): number; asWeeks(): number; months(): number; asMonths(): number; years(): number; asYears(): number; add(inp?: moment.DurationInputArg1, unit?: moment.unitOfTime.DurationConstructor | undefined): moment.Duration; subtract(inp?: moment.DurationInputArg1, unit?: moment.unitOfTime.DurationConstructor | undefined): moment.Duration; locale(): string; locale(locale: moment.LocaleSpecifier): moment.Duration; localeData(): moment.Locale; toISOString(): string; isValid(): boolean; lang(locale: moment.LocaleSpecifier): moment.Moment; lang(): moment.Locale; toIsoString(): string; format: moment.Format; }; description?: string | undefined; filter?: string | undefined; metadata?: ({ source: string; destination?: string | undefined; limit?: number | undefined; } | { source: string; destination: string; limit: number; })[] | undefined; metrics?: { name: string; metrics: ({ name: string; field: string; aggregation: ", + { + "pluginId": "@kbn/entities-schema", + "scope": "common", + "docId": "kibKbnEntitiesSchemaPluginApi", + "section": "def-common.BasicAggregations", + "text": "BasicAggregations" + }, + "; filter?: string | undefined; } | { name: string; aggregation: \"doc_count\"; filter?: string | undefined; } | { name: string; field: string; percentile: number; aggregation: \"percentile\"; filter?: string | undefined; })[]; equation: string; }[] | undefined; staticFields?: Record | undefined; settings?: { syncField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; } | undefined; }, { id: string; type: ", + { + "pluginId": "@kbn/entities-schema", + "scope": "common", + "docId": "kibKbnEntitiesSchemaPluginApi", + "section": "def-common.EntityType", + "text": "EntityType" + }, + "; name: string; indexPatterns: string[]; timestampField: string; identityFields: (string | { field: string; optional: boolean; })[]; identityTemplate: string; lookback: string; description?: string | undefined; filter?: string | undefined; metadata?: (string | { source: string; destination?: string | undefined; limit?: number | undefined; })[] | undefined; metrics?: { name: string; metrics: ({ name: string; field: string; aggregation: ", + { + "pluginId": "@kbn/entities-schema", + "scope": "common", + "docId": "kibKbnEntitiesSchemaPluginApi", + "section": "def-common.BasicAggregations", + "text": "BasicAggregations" + }, + "; filter?: string | undefined; } | { name: string; aggregation: \"doc_count\"; filter?: string | undefined; } | { name: string; field: string; percentile: number; aggregation: \"percentile\"; filter?: string | undefined; })[]; equation: string; }[] | undefined; staticFields?: Record | undefined; settings?: { syncField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; } | undefined; }>" + ], + "path": "x-pack/packages/kbn-entities-schema/src/schema/entity_definition.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/entities-schema", + "id": "def-common.entitySchema", + "type": "Object", + "tags": [], + "label": "entitySchema", + "description": [], + "signature": [ + "Zod.ZodIntersection; identityFields: Zod.ZodArray; metric: Zod.ZodRecord; }, \"strip\", Zod.ZodTypeAny, { id: string; indexPatterns: string[]; metric: Record; identityFields: string[]; }, { id: string; indexPatterns: string[]; metric: Record; identityFields: string[]; }>; }, \"strip\", Zod.ZodTypeAny, { entity: { id: string; indexPatterns: string[]; metric: Record; identityFields: string[]; }; }, { entity: { id: string; indexPatterns: string[]; metric: Record; identityFields: string[]; }; }>, Zod.ZodRecord>>" + ], + "path": "x-pack/packages/kbn-entities-schema/src/schema/entity.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/entities-schema", + "id": "def-common.entityTypeSchema", + "type": "Object", + "tags": [], + "label": "entityTypeSchema", + "description": [], + "signature": [ + "Zod.ZodNativeEnum" + ], + "path": "x-pack/packages/kbn-entities-schema/src/schema/common.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/entities-schema", + "id": "def-common.filterSchema", + "type": "Object", + "tags": [], + "label": "filterSchema", + "description": [], + "signature": [ + "Zod.ZodOptional" + ], + "path": "x-pack/packages/kbn-entities-schema/src/schema/common.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/entities-schema", + "id": "def-common.identityFieldsSchema", + "type": "Object", + "tags": [], + "label": "identityFieldsSchema", + "description": [], + "signature": [ + "Zod.ZodUnion<[Zod.ZodObject<{ field: Zod.ZodString; optional: Zod.ZodBoolean; }, \"strip\", Zod.ZodTypeAny, { field: string; optional: boolean; }, { field: string; optional: boolean; }>, Zod.ZodEffects]>" + ], + "path": "x-pack/packages/kbn-entities-schema/src/schema/common.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/entities-schema", + "id": "def-common.keyMetricSchema", + "type": "Object", + "tags": [], + "label": "keyMetricSchema", + "description": [], + "signature": [ + "Zod.ZodObject<{ name: Zod.ZodString; metrics: Zod.ZodArray; field: Zod.ZodString; filter: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { name: string; field: string; aggregation: ", + { + "pluginId": "@kbn/entities-schema", + "scope": "common", + "docId": "kibKbnEntitiesSchemaPluginApi", + "section": "def-common.BasicAggregations", + "text": "BasicAggregations" + }, + "; filter?: string | undefined; }, { name: string; field: string; aggregation: ", + { + "pluginId": "@kbn/entities-schema", + "scope": "common", + "docId": "kibKbnEntitiesSchemaPluginApi", + "section": "def-common.BasicAggregations", + "text": "BasicAggregations" + }, + "; filter?: string | undefined; }>, Zod.ZodObject<{ name: Zod.ZodString; aggregation: Zod.ZodLiteral<\"doc_count\">; filter: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { name: string; aggregation: \"doc_count\"; filter?: string | undefined; }, { name: string; aggregation: \"doc_count\"; filter?: string | undefined; }>, Zod.ZodObject<{ name: Zod.ZodString; aggregation: Zod.ZodLiteral<\"percentile\">; field: Zod.ZodString; percentile: Zod.ZodNumber; filter: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { name: string; field: string; percentile: number; aggregation: \"percentile\"; filter?: string | undefined; }, { name: string; field: string; percentile: number; aggregation: \"percentile\"; filter?: string | undefined; }>]>, \"many\">; equation: Zod.ZodString; }, \"strip\", Zod.ZodTypeAny, { name: string; metrics: ({ name: string; field: string; aggregation: ", + { + "pluginId": "@kbn/entities-schema", + "scope": "common", + "docId": "kibKbnEntitiesSchemaPluginApi", + "section": "def-common.BasicAggregations", + "text": "BasicAggregations" + }, + "; filter?: string | undefined; } | { name: string; aggregation: \"doc_count\"; filter?: string | undefined; } | { name: string; field: string; percentile: number; aggregation: \"percentile\"; filter?: string | undefined; })[]; equation: string; }, { name: string; metrics: ({ name: string; field: string; aggregation: ", + { + "pluginId": "@kbn/entities-schema", + "scope": "common", + "docId": "kibKbnEntitiesSchemaPluginApi", + "section": "def-common.BasicAggregations", + "text": "BasicAggregations" + }, + "; filter?: string | undefined; } | { name: string; aggregation: \"doc_count\"; filter?: string | undefined; } | { name: string; field: string; percentile: number; aggregation: \"percentile\"; filter?: string | undefined; })[]; equation: string; }>" + ], + "path": "x-pack/packages/kbn-entities-schema/src/schema/common.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/entities-schema", + "id": "def-common.metadataSchema", + "type": "Object", + "tags": [], + "label": "metadataSchema", + "description": [], + "signature": [ + "Zod.ZodUnion<[Zod.ZodObject<{ source: Zod.ZodString; destination: Zod.ZodOptional; limit: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { source: string; destination?: string | undefined; limit?: number | undefined; }, { source: string; destination?: string | undefined; limit?: number | undefined; }>, Zod.ZodEffects]>" + ], + "path": "x-pack/packages/kbn-entities-schema/src/schema/common.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/entities-schema", + "id": "def-common.metricSchema", + "type": "Object", + "tags": [], + "label": "metricSchema", + "description": [], + "signature": [ + "Zod.ZodDiscriminatedUnion<\"aggregation\", [Zod.ZodObject<{ name: Zod.ZodString; aggregation: Zod.ZodNativeEnum; field: Zod.ZodString; filter: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { name: string; field: string; aggregation: ", + { + "pluginId": "@kbn/entities-schema", + "scope": "common", + "docId": "kibKbnEntitiesSchemaPluginApi", + "section": "def-common.BasicAggregations", + "text": "BasicAggregations" + }, + "; filter?: string | undefined; }, { name: string; field: string; aggregation: ", + { + "pluginId": "@kbn/entities-schema", + "scope": "common", + "docId": "kibKbnEntitiesSchemaPluginApi", + "section": "def-common.BasicAggregations", + "text": "BasicAggregations" + }, + "; filter?: string | undefined; }>, Zod.ZodObject<{ name: Zod.ZodString; aggregation: Zod.ZodLiteral<\"doc_count\">; filter: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { name: string; aggregation: \"doc_count\"; filter?: string | undefined; }, { name: string; aggregation: \"doc_count\"; filter?: string | undefined; }>, Zod.ZodObject<{ name: Zod.ZodString; aggregation: Zod.ZodLiteral<\"percentile\">; field: Zod.ZodString; percentile: Zod.ZodNumber; filter: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { name: string; field: string; percentile: number; aggregation: \"percentile\"; filter?: string | undefined; }, { name: string; field: string; percentile: number; aggregation: \"percentile\"; filter?: string | undefined; }>]>" + ], + "path": "x-pack/packages/kbn-entities-schema/src/schema/common.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/entities-schema", + "id": "def-common.percentileMetricSchema", + "type": "Object", + "tags": [], + "label": "percentileMetricSchema", + "description": [], + "signature": [ + "Zod.ZodObject<{ name: Zod.ZodString; aggregation: Zod.ZodLiteral<\"percentile\">; field: Zod.ZodString; percentile: Zod.ZodNumber; filter: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { name: string; field: string; percentile: number; aggregation: \"percentile\"; filter?: string | undefined; }, { name: string; field: string; percentile: number; aggregation: \"percentile\"; filter?: string | undefined; }>" + ], + "path": "x-pack/packages/kbn-entities-schema/src/schema/common.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ] + } +} \ No newline at end of file diff --git a/api_docs/kbn_entities_schema.mdx b/api_docs/kbn_entities_schema.mdx new file mode 100644 index 0000000000000..7fdc3856050d6 --- /dev/null +++ b/api_docs/kbn_entities_schema.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: kibKbnEntitiesSchemaPluginApi +slug: /kibana-dev-docs/api/kbn-entities-schema +title: "@kbn/entities-schema" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/entities-schema plugin +date: 2024-05-20 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/entities-schema'] +--- +import kbnEntitiesSchemaObj from './kbn_entities_schema.devdocs.json'; + + + +Contact [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs-knowledge-team) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 19 | 0 | 19 | 0 | + +## Common + +### Objects + + +### Enums + + +### Consts, variables and types + + diff --git a/api_docs/kbn_es.devdocs.json b/api_docs/kbn_es.devdocs.json index 434582f0c7a68..64d416e95d6c5 100644 --- a/api_docs/kbn_es.devdocs.json +++ b/api_docs/kbn_es.devdocs.json @@ -81,7 +81,7 @@ "signature": [ "(options: ", "InstallSourceOptions", - ") => Promise<{ installPath: string; }>" + ") => Promise<{ installPath: string; disableEsTmpDir: boolean; }>" ], "path": "packages/kbn-es/src/cluster.ts", "deprecated": false, @@ -153,7 +153,7 @@ "signature": [ "(options: ", "InstallSnapshotOptions", - ") => Promise<{ installPath: string; }>" + ") => Promise<{ installPath: string; disableEsTmpDir: boolean; }>" ], "path": "packages/kbn-es/src/cluster.ts", "deprecated": false, @@ -189,7 +189,7 @@ "signature": [ "(archivePath: string, options?: ", "InstallArchiveOptions", - " | undefined) => Promise<{ installPath: string; }>" + " | undefined) => Promise<{ installPath: string; disableEsTmpDir: boolean; }>" ], "path": "packages/kbn-es/src/cluster.ts", "deprecated": false, diff --git a/api_docs/kbn_es.mdx b/api_docs/kbn_es.mdx index bbe24a2dccc9b..7509c63e77921 100644 --- a/api_docs/kbn_es.mdx +++ b/api_docs/kbn_es.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es title: "@kbn/es" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es plugin -date: 2024-05-16 +date: 2024-05-20 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 9b179f7ab7e87..f6680c048cf14 100644 --- a/api_docs/kbn_es_archiver.mdx +++ b/api_docs/kbn_es_archiver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-archiver title: "@kbn/es-archiver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-archiver plugin -date: 2024-05-16 +date: 2024-05-20 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 560f2b4d0efe0..f49662d452ced 100644 --- a/api_docs/kbn_es_errors.mdx +++ b/api_docs/kbn_es_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-errors title: "@kbn/es-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-errors plugin -date: 2024-05-16 +date: 2024-05-20 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 a87e0d01ae416..1f865d800f153 100644 --- a/api_docs/kbn_es_query.mdx +++ b/api_docs/kbn_es_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-query title: "@kbn/es-query" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-query plugin -date: 2024-05-16 +date: 2024-05-20 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 f2560bdbe8d85..8a65e5353d999 100644 --- a/api_docs/kbn_es_types.mdx +++ b/api_docs/kbn_es_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-types title: "@kbn/es-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-types plugin -date: 2024-05-16 +date: 2024-05-20 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 c6c6c0611a875..f9894eb340d78 100644 --- a/api_docs/kbn_eslint_plugin_imports.mdx +++ b/api_docs/kbn_eslint_plugin_imports.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-eslint-plugin-imports title: "@kbn/eslint-plugin-imports" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/eslint-plugin-imports plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_esql_ast.mdx b/api_docs/kbn_esql_ast.mdx index 0d937df6592c8..c73424f7578d0 100644 --- a/api_docs/kbn_esql_ast.mdx +++ b/api_docs/kbn_esql_ast.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-ast title: "@kbn/esql-ast" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-ast plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-ast'] --- import kbnEsqlAstObj from './kbn_esql_ast.devdocs.json'; diff --git a/api_docs/kbn_esql_utils.devdocs.json b/api_docs/kbn_esql_utils.devdocs.json index 095c88e18a505..db7a44d058d30 100644 --- a/api_docs/kbn_esql_utils.devdocs.json +++ b/api_docs/kbn_esql_utils.devdocs.json @@ -75,7 +75,7 @@ "label": "appendWhereClauseToESQLQuery", "description": [], "signature": [ - "(baseESQLQuery: string, field: string, value: unknown, operation: \"+\" | \"-\" | \"_exists_\", fieldType: string | undefined) => string" + "(baseESQLQuery: string, field: string, value: unknown, operation: \"+\" | \"-\" | \"is_not_null\" | \"is_null\", fieldType: string | undefined) => string" ], "path": "packages/kbn-esql-utils/src/utils/append_to_query.ts", "deprecated": false, @@ -134,7 +134,7 @@ "label": "operation", "description": [], "signature": [ - "\"+\" | \"-\" | \"_exists_\"" + "\"+\" | \"-\" | \"is_not_null\" | \"is_null\"" ], "path": "packages/kbn-esql-utils/src/utils/append_to_query.ts", "deprecated": false, diff --git a/api_docs/kbn_esql_utils.mdx b/api_docs/kbn_esql_utils.mdx index 814e18f14cf5e..3185b83461179 100644 --- a/api_docs/kbn_esql_utils.mdx +++ b/api_docs/kbn_esql_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-utils title: "@kbn/esql-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-utils plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-utils'] --- import kbnEsqlUtilsObj from './kbn_esql_utils.devdocs.json'; diff --git a/api_docs/kbn_esql_validation_autocomplete.mdx b/api_docs/kbn_esql_validation_autocomplete.mdx index 91e3ead1350fd..6891661b0b28c 100644 --- a/api_docs/kbn_esql_validation_autocomplete.mdx +++ b/api_docs/kbn_esql_validation_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-validation-autocomplete title: "@kbn/esql-validation-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-validation-autocomplete plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-validation-autocomplete'] --- import kbnEsqlValidationAutocompleteObj from './kbn_esql_validation_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_event_annotation_common.mdx b/api_docs/kbn_event_annotation_common.mdx index 7d831c380ee2c..bee7e2673330f 100644 --- a/api_docs/kbn_event_annotation_common.mdx +++ b/api_docs/kbn_event_annotation_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-event-annotation-common title: "@kbn/event-annotation-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/event-annotation-common plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/event-annotation-common'] --- import kbnEventAnnotationCommonObj from './kbn_event_annotation_common.devdocs.json'; diff --git a/api_docs/kbn_event_annotation_components.mdx b/api_docs/kbn_event_annotation_components.mdx index ddcba759bd1bb..a0edca96484e4 100644 --- a/api_docs/kbn_event_annotation_components.mdx +++ b/api_docs/kbn_event_annotation_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-event-annotation-components title: "@kbn/event-annotation-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/event-annotation-components plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/event-annotation-components'] --- import kbnEventAnnotationComponentsObj from './kbn_event_annotation_components.devdocs.json'; diff --git a/api_docs/kbn_expandable_flyout.mdx b/api_docs/kbn_expandable_flyout.mdx index 3d971abf8407c..4f18c95f49ca2 100644 --- a/api_docs/kbn_expandable_flyout.mdx +++ b/api_docs/kbn_expandable_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-expandable-flyout title: "@kbn/expandable-flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/expandable-flyout plugin -date: 2024-05-16 +date: 2024-05-20 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 04ad5a32c856b..8b92a98dab6e5 100644 --- a/api_docs/kbn_field_types.mdx +++ b/api_docs/kbn_field_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-types title: "@kbn/field-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-types plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-types'] --- import kbnFieldTypesObj from './kbn_field_types.devdocs.json'; diff --git a/api_docs/kbn_field_utils.mdx b/api_docs/kbn_field_utils.mdx index 633fb83ee7041..e1cb1259a8b1b 100644 --- a/api_docs/kbn_field_utils.mdx +++ b/api_docs/kbn_field_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-utils title: "@kbn/field-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-utils plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-utils'] --- import kbnFieldUtilsObj from './kbn_field_utils.devdocs.json'; diff --git a/api_docs/kbn_find_used_node_modules.mdx b/api_docs/kbn_find_used_node_modules.mdx index ae3ee63ee0574..3724a34fad00b 100644 --- a/api_docs/kbn_find_used_node_modules.mdx +++ b/api_docs/kbn_find_used_node_modules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-find-used-node-modules title: "@kbn/find-used-node-modules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/find-used-node-modules plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/find-used-node-modules'] --- import kbnFindUsedNodeModulesObj from './kbn_find_used_node_modules.devdocs.json'; diff --git a/api_docs/kbn_formatters.mdx b/api_docs/kbn_formatters.mdx index 8a48773d6a634..648c023f14d6f 100644 --- a/api_docs/kbn_formatters.mdx +++ b/api_docs/kbn_formatters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-formatters title: "@kbn/formatters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/formatters plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/formatters'] --- import kbnFormattersObj from './kbn_formatters.devdocs.json'; diff --git a/api_docs/kbn_ftr_common_functional_services.mdx b/api_docs/kbn_ftr_common_functional_services.mdx index 01e0892d148d9..b545e7c3c5b34 100644 --- a/api_docs/kbn_ftr_common_functional_services.mdx +++ b/api_docs/kbn_ftr_common_functional_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-services title: "@kbn/ftr-common-functional-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-services plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ftr-common-functional-services'] --- import kbnFtrCommonFunctionalServicesObj from './kbn_ftr_common_functional_services.devdocs.json'; diff --git a/api_docs/kbn_ftr_common_functional_ui_services.mdx b/api_docs/kbn_ftr_common_functional_ui_services.mdx index e8528064fd964..6681c12e8d111 100644 --- a/api_docs/kbn_ftr_common_functional_ui_services.mdx +++ b/api_docs/kbn_ftr_common_functional_ui_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-ui-services title: "@kbn/ftr-common-functional-ui-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-ui-services plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ftr-common-functional-ui-services'] --- import kbnFtrCommonFunctionalUiServicesObj from './kbn_ftr_common_functional_ui_services.devdocs.json'; diff --git a/api_docs/kbn_generate.mdx b/api_docs/kbn_generate.mdx index 2fcf761909de9..9ac51a310dad3 100644 --- a/api_docs/kbn_generate.mdx +++ b/api_docs/kbn_generate.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate title: "@kbn/generate" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate plugin -date: 2024-05-16 +date: 2024-05-20 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 6cc4a893d35c9..068cf2df7e15f 100644 --- a/api_docs/kbn_generate_console_definitions.mdx +++ b/api_docs/kbn_generate_console_definitions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-console-definitions title: "@kbn/generate-console-definitions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-console-definitions plugin -date: 2024-05-16 +date: 2024-05-20 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 b7e80fcd2dd84..62cc1c4471aa0 100644 --- a/api_docs/kbn_generate_csv.mdx +++ b/api_docs/kbn_generate_csv.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-csv title: "@kbn/generate-csv" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-csv plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-csv'] --- import kbnGenerateCsvObj from './kbn_generate_csv.devdocs.json'; diff --git a/api_docs/kbn_guided_onboarding.mdx b/api_docs/kbn_guided_onboarding.mdx index 15657f39b8393..4ccfe0bb2217b 100644 --- a/api_docs/kbn_guided_onboarding.mdx +++ b/api_docs/kbn_guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-guided-onboarding title: "@kbn/guided-onboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/guided-onboarding plugin -date: 2024-05-16 +date: 2024-05-20 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 1a246c914a016..434c0ea1c88f6 100644 --- a/api_docs/kbn_handlebars.mdx +++ b/api_docs/kbn_handlebars.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-handlebars title: "@kbn/handlebars" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/handlebars plugin -date: 2024-05-16 +date: 2024-05-20 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 24b6ad03908c1..e4c64d5be2625 100644 --- a/api_docs/kbn_hapi_mocks.mdx +++ b/api_docs/kbn_hapi_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-hapi-mocks title: "@kbn/hapi-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/hapi-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 06d2ac4f3792a..bc245facc9d6b 100644 --- a/api_docs/kbn_health_gateway_server.mdx +++ b/api_docs/kbn_health_gateway_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-health-gateway-server title: "@kbn/health-gateway-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/health-gateway-server plugin -date: 2024-05-16 +date: 2024-05-20 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 09ac4afcf3651..f5260f6eb7e11 100644 --- a/api_docs/kbn_home_sample_data_card.mdx +++ b/api_docs/kbn_home_sample_data_card.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-card title: "@kbn/home-sample-data-card" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-card plugin -date: 2024-05-16 +date: 2024-05-20 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 9e8a77a0bde68..bb719406e334e 100644 --- a/api_docs/kbn_home_sample_data_tab.mdx +++ b/api_docs/kbn_home_sample_data_tab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-tab title: "@kbn/home-sample-data-tab" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-tab plugin -date: 2024-05-16 +date: 2024-05-20 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 b80c8b49f39a8..a0bfa7bf67e25 100644 --- a/api_docs/kbn_i18n.mdx +++ b/api_docs/kbn_i18n.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n title: "@kbn/i18n" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n plugin -date: 2024-05-16 +date: 2024-05-20 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 b2e6437117528..7c4c02c29dede 100644 --- a/api_docs/kbn_i18n_react.mdx +++ b/api_docs/kbn_i18n_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n-react title: "@kbn/i18n-react" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n-react plugin -date: 2024-05-16 +date: 2024-05-20 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 fc4e4023935bf..eaa27c6b9303a 100644 --- a/api_docs/kbn_import_resolver.mdx +++ b/api_docs/kbn_import_resolver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-import-resolver title: "@kbn/import-resolver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/import-resolver plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_index_management.mdx b/api_docs/kbn_index_management.mdx index 522045b7c9681..aad52fba0f782 100644 --- a/api_docs/kbn_index_management.mdx +++ b/api_docs/kbn_index_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-index-management title: "@kbn/index-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/index-management plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/index-management'] --- import kbnIndexManagementObj from './kbn_index_management.devdocs.json'; diff --git a/api_docs/kbn_inference_integration_flyout.mdx b/api_docs/kbn_inference_integration_flyout.mdx index a03b8fb0dfb9e..dae37620023b2 100644 --- a/api_docs/kbn_inference_integration_flyout.mdx +++ b/api_docs/kbn_inference_integration_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-inference_integration_flyout title: "@kbn/inference_integration_flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/inference_integration_flyout plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/inference_integration_flyout'] --- import kbnInferenceIntegrationFlyoutObj from './kbn_inference_integration_flyout.devdocs.json'; diff --git a/api_docs/kbn_infra_forge.mdx b/api_docs/kbn_infra_forge.mdx index 5eb8b76dfc8db..8252010eecb30 100644 --- a/api_docs/kbn_infra_forge.mdx +++ b/api_docs/kbn_infra_forge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-infra-forge title: "@kbn/infra-forge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/infra-forge plugin -date: 2024-05-16 +date: 2024-05-20 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 c90abbce90fa1..13232cc3f0076 100644 --- a/api_docs/kbn_interpreter.mdx +++ b/api_docs/kbn_interpreter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-interpreter title: "@kbn/interpreter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/interpreter plugin -date: 2024-05-16 +date: 2024-05-20 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 9cf87176d5f1b..387b707ef33bf 100644 --- a/api_docs/kbn_io_ts_utils.mdx +++ b/api_docs/kbn_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-io-ts-utils title: "@kbn/io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/io-ts-utils plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils'] --- import kbnIoTsUtilsObj from './kbn_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_ipynb.mdx b/api_docs/kbn_ipynb.mdx index b203fc2516fdb..5e5cf0f6b7570 100644 --- a/api_docs/kbn_ipynb.mdx +++ b/api_docs/kbn_ipynb.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ipynb title: "@kbn/ipynb" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ipynb plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ipynb'] --- import kbnIpynbObj from './kbn_ipynb.devdocs.json'; diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx index fe5622055d71b..9d53dc3928e03 100644 --- a/api_docs/kbn_jest_serializers.mdx +++ b/api_docs/kbn_jest_serializers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-jest-serializers title: "@kbn/jest-serializers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/jest-serializers plugin -date: 2024-05-16 +date: 2024-05-20 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 aa9f296afcfb2..a88d3370f8737 100644 --- a/api_docs/kbn_journeys.mdx +++ b/api_docs/kbn_journeys.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-journeys title: "@kbn/journeys" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/journeys plugin -date: 2024-05-16 +date: 2024-05-20 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 fe5c456b61fc0..70a94bda1b19d 100644 --- a/api_docs/kbn_json_ast.mdx +++ b/api_docs/kbn_json_ast.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-json-ast title: "@kbn/json-ast" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/json-ast plugin -date: 2024-05-16 +date: 2024-05-20 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 1f3a9b93b94f5..73e8a08a8db27 100644 --- a/api_docs/kbn_kibana_manifest_schema.mdx +++ b/api_docs/kbn_kibana_manifest_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-kibana-manifest-schema title: "@kbn/kibana-manifest-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/kibana-manifest-schema plugin -date: 2024-05-16 +date: 2024-05-20 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 1fcb554a8919b..b5e3a51ddc953 100644 --- a/api_docs/kbn_language_documentation_popover.mdx +++ b/api_docs/kbn_language_documentation_popover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-language-documentation-popover title: "@kbn/language-documentation-popover" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/language-documentation-popover plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/language-documentation-popover'] --- import kbnLanguageDocumentationPopoverObj from './kbn_language_documentation_popover.devdocs.json'; diff --git a/api_docs/kbn_lens_embeddable_utils.mdx b/api_docs/kbn_lens_embeddable_utils.mdx index d041f048782be..d7cff9a698593 100644 --- a/api_docs/kbn_lens_embeddable_utils.mdx +++ b/api_docs/kbn_lens_embeddable_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-lens-embeddable-utils title: "@kbn/lens-embeddable-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/lens-embeddable-utils plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/lens-embeddable-utils'] --- import kbnLensEmbeddableUtilsObj from './kbn_lens_embeddable_utils.devdocs.json'; diff --git a/api_docs/kbn_lens_formula_docs.mdx b/api_docs/kbn_lens_formula_docs.mdx index 6e8990daf040c..06dae205273db 100644 --- a/api_docs/kbn_lens_formula_docs.mdx +++ b/api_docs/kbn_lens_formula_docs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-lens-formula-docs title: "@kbn/lens-formula-docs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/lens-formula-docs plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/lens-formula-docs'] --- import kbnLensFormulaDocsObj from './kbn_lens_formula_docs.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index af1f09d93641b..fa0cc30a66279 100644 --- a/api_docs/kbn_logging.mdx +++ b/api_docs/kbn_logging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging title: "@kbn/logging" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging plugin -date: 2024-05-16 +date: 2024-05-20 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 f85178aa2f7c0..3a2c914d8972f 100644 --- a/api_docs/kbn_logging_mocks.mdx +++ b/api_docs/kbn_logging_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging-mocks title: "@kbn/logging-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging-mocks plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks'] --- import kbnLoggingMocksObj from './kbn_logging_mocks.devdocs.json'; diff --git a/api_docs/kbn_managed_content_badge.mdx b/api_docs/kbn_managed_content_badge.mdx index 45c75bd11ea9c..c853f7684ffdf 100644 --- a/api_docs/kbn_managed_content_badge.mdx +++ b/api_docs/kbn_managed_content_badge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-content-badge title: "@kbn/managed-content-badge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-content-badge plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-content-badge'] --- import kbnManagedContentBadgeObj from './kbn_managed_content_badge.devdocs.json'; diff --git a/api_docs/kbn_managed_vscode_config.mdx b/api_docs/kbn_managed_vscode_config.mdx index 49419565ee1da..bb5564aa4a8ed 100644 --- a/api_docs/kbn_managed_vscode_config.mdx +++ b/api_docs/kbn_managed_vscode_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-vscode-config title: "@kbn/managed-vscode-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-vscode-config plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-vscode-config'] --- import kbnManagedVscodeConfigObj from './kbn_managed_vscode_config.devdocs.json'; diff --git a/api_docs/kbn_management_cards_navigation.mdx b/api_docs/kbn_management_cards_navigation.mdx index e56190abe6db0..a26885eaf8dfc 100644 --- a/api_docs/kbn_management_cards_navigation.mdx +++ b/api_docs/kbn_management_cards_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-cards-navigation title: "@kbn/management-cards-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-cards-navigation plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-cards-navigation'] --- import kbnManagementCardsNavigationObj from './kbn_management_cards_navigation.devdocs.json'; diff --git a/api_docs/kbn_management_settings_application.mdx b/api_docs/kbn_management_settings_application.mdx index 6e2a342d7864f..9a8776cf9eac4 100644 --- a/api_docs/kbn_management_settings_application.mdx +++ b/api_docs/kbn_management_settings_application.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-application title: "@kbn/management-settings-application" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-application plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-application'] --- import kbnManagementSettingsApplicationObj from './kbn_management_settings_application.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_field_category.mdx b/api_docs/kbn_management_settings_components_field_category.mdx index 1f565f99ba23d..20fab4480b4df 100644 --- a/api_docs/kbn_management_settings_components_field_category.mdx +++ b/api_docs/kbn_management_settings_components_field_category.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-category title: "@kbn/management-settings-components-field-category" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-category plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-field-category'] --- import kbnManagementSettingsComponentsFieldCategoryObj from './kbn_management_settings_components_field_category.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_field_input.mdx b/api_docs/kbn_management_settings_components_field_input.mdx index 8aadf9f1d589c..3319f91551d56 100644 --- a/api_docs/kbn_management_settings_components_field_input.mdx +++ b/api_docs/kbn_management_settings_components_field_input.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-input title: "@kbn/management-settings-components-field-input" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-input plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-field-input'] --- import kbnManagementSettingsComponentsFieldInputObj from './kbn_management_settings_components_field_input.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_field_row.mdx b/api_docs/kbn_management_settings_components_field_row.mdx index fefcefacd32c6..0103222f036d7 100644 --- a/api_docs/kbn_management_settings_components_field_row.mdx +++ b/api_docs/kbn_management_settings_components_field_row.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-row title: "@kbn/management-settings-components-field-row" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-row plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-field-row'] --- import kbnManagementSettingsComponentsFieldRowObj from './kbn_management_settings_components_field_row.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_form.mdx b/api_docs/kbn_management_settings_components_form.mdx index fd44b889d251a..d095a0f3ce8a3 100644 --- a/api_docs/kbn_management_settings_components_form.mdx +++ b/api_docs/kbn_management_settings_components_form.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-form title: "@kbn/management-settings-components-form" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-form plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-form'] --- import kbnManagementSettingsComponentsFormObj from './kbn_management_settings_components_form.devdocs.json'; diff --git a/api_docs/kbn_management_settings_field_definition.mdx b/api_docs/kbn_management_settings_field_definition.mdx index 961e7b7e26409..0395cea532071 100644 --- a/api_docs/kbn_management_settings_field_definition.mdx +++ b/api_docs/kbn_management_settings_field_definition.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-field-definition title: "@kbn/management-settings-field-definition" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-field-definition plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-field-definition'] --- import kbnManagementSettingsFieldDefinitionObj from './kbn_management_settings_field_definition.devdocs.json'; diff --git a/api_docs/kbn_management_settings_ids.devdocs.json b/api_docs/kbn_management_settings_ids.devdocs.json index 4495e0bc68ae0..b4b24972b6989 100644 --- a/api_docs/kbn_management_settings_ids.devdocs.json +++ b/api_docs/kbn_management_settings_ids.devdocs.json @@ -1342,6 +1342,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/management-settings-ids", + "id": "def-common.OBSERVABILITY_ENABLE_CONTAINER_ASSET_VIEW_ID", + "type": "string", + "tags": [], + "label": "OBSERVABILITY_ENABLE_CONTAINER_ASSET_VIEW_ID", + "description": [], + "signature": [ + "\"observability:enableContainerAssetView\"" + ], + "path": "packages/kbn-management/settings/setting_ids/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/management-settings-ids", "id": "def-common.OBSERVABILITY_ENABLE_INFRASTRUCTURE_ASSET_CUSTOM_DASHBOARDS_ID", diff --git a/api_docs/kbn_management_settings_ids.mdx b/api_docs/kbn_management_settings_ids.mdx index 297d57dcf33c2..1ab0cdfdfc766 100644 --- a/api_docs/kbn_management_settings_ids.mdx +++ b/api_docs/kbn_management_settings_ids.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-ids title: "@kbn/management-settings-ids" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-ids plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-ids'] --- import kbnManagementSettingsIdsObj from './kbn_management_settings_ids.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/appex-sharedux @elastic/kibana-management](https://github.com/ | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 138 | 0 | 135 | 0 | +| 139 | 0 | 136 | 0 | ## Common diff --git a/api_docs/kbn_management_settings_section_registry.mdx b/api_docs/kbn_management_settings_section_registry.mdx index 0399496f900ea..c1568de1b76f5 100644 --- a/api_docs/kbn_management_settings_section_registry.mdx +++ b/api_docs/kbn_management_settings_section_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-section-registry title: "@kbn/management-settings-section-registry" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-section-registry plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-section-registry'] --- import kbnManagementSettingsSectionRegistryObj from './kbn_management_settings_section_registry.devdocs.json'; diff --git a/api_docs/kbn_management_settings_types.mdx b/api_docs/kbn_management_settings_types.mdx index 49180bbb02e57..108d8de81ef67 100644 --- a/api_docs/kbn_management_settings_types.mdx +++ b/api_docs/kbn_management_settings_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-types title: "@kbn/management-settings-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-types plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-types'] --- import kbnManagementSettingsTypesObj from './kbn_management_settings_types.devdocs.json'; diff --git a/api_docs/kbn_management_settings_utilities.mdx b/api_docs/kbn_management_settings_utilities.mdx index da99f5e9f1271..3f1ebe9b73274 100644 --- a/api_docs/kbn_management_settings_utilities.mdx +++ b/api_docs/kbn_management_settings_utilities.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-utilities title: "@kbn/management-settings-utilities" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-utilities plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-utilities'] --- import kbnManagementSettingsUtilitiesObj from './kbn_management_settings_utilities.devdocs.json'; diff --git a/api_docs/kbn_management_storybook_config.mdx b/api_docs/kbn_management_storybook_config.mdx index de5c1110c2404..721d1e5c06122 100644 --- a/api_docs/kbn_management_storybook_config.mdx +++ b/api_docs/kbn_management_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-storybook-config title: "@kbn/management-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-storybook-config plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-storybook-config'] --- import kbnManagementStorybookConfigObj from './kbn_management_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index abb534d9be1a2..f36575c2e9e0f 100644 --- a/api_docs/kbn_mapbox_gl.mdx +++ b/api_docs/kbn_mapbox_gl.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mapbox-gl title: "@kbn/mapbox-gl" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mapbox-gl plugin -date: 2024-05-16 +date: 2024-05-20 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 e952df482cf47..944c37620fca0 100644 --- a/api_docs/kbn_maps_vector_tile_utils.mdx +++ b/api_docs/kbn_maps_vector_tile_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-maps-vector-tile-utils title: "@kbn/maps-vector-tile-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/maps-vector-tile-utils plugin -date: 2024-05-16 +date: 2024-05-20 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 ac43b164b2534..691f13089edfc 100644 --- a/api_docs/kbn_ml_agg_utils.mdx +++ b/api_docs/kbn_ml_agg_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-agg-utils title: "@kbn/ml-agg-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-agg-utils plugin -date: 2024-05-16 +date: 2024-05-20 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 d5ea92226e837..4627bcb5da05d 100644 --- a/api_docs/kbn_ml_anomaly_utils.mdx +++ b/api_docs/kbn_ml_anomaly_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-anomaly-utils title: "@kbn/ml-anomaly-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-anomaly-utils plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-anomaly-utils'] --- import kbnMlAnomalyUtilsObj from './kbn_ml_anomaly_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_cancellable_search.mdx b/api_docs/kbn_ml_cancellable_search.mdx index 8aaca7383fda2..c534bb98129bf 100644 --- a/api_docs/kbn_ml_cancellable_search.mdx +++ b/api_docs/kbn_ml_cancellable_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-cancellable-search title: "@kbn/ml-cancellable-search" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-cancellable-search plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-cancellable-search'] --- import kbnMlCancellableSearchObj from './kbn_ml_cancellable_search.devdocs.json'; diff --git a/api_docs/kbn_ml_category_validator.mdx b/api_docs/kbn_ml_category_validator.mdx index cfff61a6d388c..cf13409ca1bc2 100644 --- a/api_docs/kbn_ml_category_validator.mdx +++ b/api_docs/kbn_ml_category_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-category-validator title: "@kbn/ml-category-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-category-validator plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-category-validator'] --- import kbnMlCategoryValidatorObj from './kbn_ml_category_validator.devdocs.json'; diff --git a/api_docs/kbn_ml_chi2test.mdx b/api_docs/kbn_ml_chi2test.mdx index 066dd0af7acd2..96ef4dce826c2 100644 --- a/api_docs/kbn_ml_chi2test.mdx +++ b/api_docs/kbn_ml_chi2test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-chi2test title: "@kbn/ml-chi2test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-chi2test plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-chi2test'] --- import kbnMlChi2testObj from './kbn_ml_chi2test.devdocs.json'; diff --git a/api_docs/kbn_ml_data_frame_analytics_utils.mdx b/api_docs/kbn_ml_data_frame_analytics_utils.mdx index 1dc691ed14ac1..2e815aec2dfba 100644 --- a/api_docs/kbn_ml_data_frame_analytics_utils.mdx +++ b/api_docs/kbn_ml_data_frame_analytics_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-frame-analytics-utils title: "@kbn/ml-data-frame-analytics-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-frame-analytics-utils plugin -date: 2024-05-16 +date: 2024-05-20 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 68beb5c99a0b1..06944e51e96fe 100644 --- a/api_docs/kbn_ml_data_grid.mdx +++ b/api_docs/kbn_ml_data_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-grid title: "@kbn/ml-data-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-grid plugin -date: 2024-05-16 +date: 2024-05-20 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 2ec314b6d2f2f..54c601b060b36 100644 --- a/api_docs/kbn_ml_date_picker.mdx +++ b/api_docs/kbn_ml_date_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-picker title: "@kbn/ml-date-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-picker plugin -date: 2024-05-16 +date: 2024-05-20 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 bc36c983a8e58..726a77c042d8e 100644 --- a/api_docs/kbn_ml_date_utils.mdx +++ b/api_docs/kbn_ml_date_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-utils title: "@kbn/ml-date-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-utils plugin -date: 2024-05-16 +date: 2024-05-20 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 54904f9dcf01a..bb0a28fae804b 100644 --- a/api_docs/kbn_ml_error_utils.mdx +++ b/api_docs/kbn_ml_error_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-error-utils title: "@kbn/ml-error-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-error-utils plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-error-utils'] --- import kbnMlErrorUtilsObj from './kbn_ml_error_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_in_memory_table.mdx b/api_docs/kbn_ml_in_memory_table.mdx index ace9ba9a7a16c..8256a5b17dccc 100644 --- a/api_docs/kbn_ml_in_memory_table.mdx +++ b/api_docs/kbn_ml_in_memory_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-in-memory-table title: "@kbn/ml-in-memory-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-in-memory-table plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-in-memory-table'] --- import kbnMlInMemoryTableObj from './kbn_ml_in_memory_table.devdocs.json'; diff --git a/api_docs/kbn_ml_is_defined.mdx b/api_docs/kbn_ml_is_defined.mdx index 6cc9a25f58261..7d2b3849c30b2 100644 --- a/api_docs/kbn_ml_is_defined.mdx +++ b/api_docs/kbn_ml_is_defined.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-defined title: "@kbn/ml-is-defined" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-defined plugin -date: 2024-05-16 +date: 2024-05-20 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 be7e72f1acd6e..6727da83c1138 100644 --- a/api_docs/kbn_ml_is_populated_object.mdx +++ b/api_docs/kbn_ml_is_populated_object.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-populated-object title: "@kbn/ml-is-populated-object" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-populated-object plugin -date: 2024-05-16 +date: 2024-05-20 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 e42d2355c629e..ab24b887d9836 100644 --- a/api_docs/kbn_ml_kibana_theme.mdx +++ b/api_docs/kbn_ml_kibana_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-kibana-theme title: "@kbn/ml-kibana-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-kibana-theme plugin -date: 2024-05-16 +date: 2024-05-20 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 df050e23b8ea3..eec4d49078e26 100644 --- a/api_docs/kbn_ml_local_storage.mdx +++ b/api_docs/kbn_ml_local_storage.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-local-storage title: "@kbn/ml-local-storage" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-local-storage plugin -date: 2024-05-16 +date: 2024-05-20 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 bcddcebaee35b..1038de63310e5 100644 --- a/api_docs/kbn_ml_nested_property.mdx +++ b/api_docs/kbn_ml_nested_property.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-nested-property title: "@kbn/ml-nested-property" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-nested-property plugin -date: 2024-05-16 +date: 2024-05-20 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 cd110f90c86da..e1abbae79cd0a 100644 --- a/api_docs/kbn_ml_number_utils.mdx +++ b/api_docs/kbn_ml_number_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-number-utils title: "@kbn/ml-number-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-number-utils plugin -date: 2024-05-16 +date: 2024-05-20 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 e690148f10f0e..e342bce4e66e6 100644 --- a/api_docs/kbn_ml_query_utils.mdx +++ b/api_docs/kbn_ml_query_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-query-utils title: "@kbn/ml-query-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-query-utils plugin -date: 2024-05-16 +date: 2024-05-20 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 8fa568ecb2db4..304cbc9d0c8a9 100644 --- a/api_docs/kbn_ml_random_sampler_utils.mdx +++ b/api_docs/kbn_ml_random_sampler_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-random-sampler-utils title: "@kbn/ml-random-sampler-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-random-sampler-utils plugin -date: 2024-05-16 +date: 2024-05-20 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 3f488dc0d0f82..210206057ace2 100644 --- a/api_docs/kbn_ml_route_utils.mdx +++ b/api_docs/kbn_ml_route_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-route-utils title: "@kbn/ml-route-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-route-utils plugin -date: 2024-05-16 +date: 2024-05-20 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 f39a80437742a..0387d847c8dab 100644 --- a/api_docs/kbn_ml_runtime_field_utils.mdx +++ b/api_docs/kbn_ml_runtime_field_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-runtime-field-utils title: "@kbn/ml-runtime-field-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-runtime-field-utils plugin -date: 2024-05-16 +date: 2024-05-20 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 7660ee4fbce76..6fcda645376cf 100644 --- a/api_docs/kbn_ml_string_hash.mdx +++ b/api_docs/kbn_ml_string_hash.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-string-hash title: "@kbn/ml-string-hash" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-string-hash plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] --- import kbnMlStringHashObj from './kbn_ml_string_hash.devdocs.json'; diff --git a/api_docs/kbn_ml_time_buckets.mdx b/api_docs/kbn_ml_time_buckets.mdx index 0b10962df3c63..75a6555735baf 100644 --- a/api_docs/kbn_ml_time_buckets.mdx +++ b/api_docs/kbn_ml_time_buckets.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-time-buckets title: "@kbn/ml-time-buckets" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-time-buckets plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-time-buckets'] --- import kbnMlTimeBucketsObj from './kbn_ml_time_buckets.devdocs.json'; diff --git a/api_docs/kbn_ml_trained_models_utils.mdx b/api_docs/kbn_ml_trained_models_utils.mdx index 446045a85c68d..461abb8754801 100644 --- a/api_docs/kbn_ml_trained_models_utils.mdx +++ b/api_docs/kbn_ml_trained_models_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-trained-models-utils title: "@kbn/ml-trained-models-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-trained-models-utils plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-trained-models-utils'] --- import kbnMlTrainedModelsUtilsObj from './kbn_ml_trained_models_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_ui_actions.mdx b/api_docs/kbn_ml_ui_actions.mdx index 00fb6b3e1c9a4..caae8ac209814 100644 --- a/api_docs/kbn_ml_ui_actions.mdx +++ b/api_docs/kbn_ml_ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-ui-actions title: "@kbn/ml-ui-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-ui-actions plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-ui-actions'] --- import kbnMlUiActionsObj from './kbn_ml_ui_actions.devdocs.json'; diff --git a/api_docs/kbn_ml_url_state.mdx b/api_docs/kbn_ml_url_state.mdx index 0623299e03f7e..14fab7aad9c26 100644 --- a/api_docs/kbn_ml_url_state.mdx +++ b/api_docs/kbn_ml_url_state.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-url-state title: "@kbn/ml-url-state" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-url-state plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-url-state'] --- import kbnMlUrlStateObj from './kbn_ml_url_state.devdocs.json'; diff --git a/api_docs/kbn_mock_idp_utils.mdx b/api_docs/kbn_mock_idp_utils.mdx index 9292911bd5c30..d1435c0524254 100644 --- a/api_docs/kbn_mock_idp_utils.mdx +++ b/api_docs/kbn_mock_idp_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mock-idp-utils title: "@kbn/mock-idp-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mock-idp-utils plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mock-idp-utils'] --- import kbnMockIdpUtilsObj from './kbn_mock_idp_utils.devdocs.json'; diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index f16ab074bb6cc..f3f7c2bf7b17a 100644 --- a/api_docs/kbn_monaco.mdx +++ b/api_docs/kbn_monaco.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-monaco title: "@kbn/monaco" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/monaco plugin -date: 2024-05-16 +date: 2024-05-20 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 9c4da7bb006fb..54bda828e2d26 100644 --- a/api_docs/kbn_object_versioning.mdx +++ b/api_docs/kbn_object_versioning.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-object-versioning title: "@kbn/object-versioning" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/object-versioning plugin -date: 2024-05-16 +date: 2024-05-20 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 2a259d02cbe57..9ddf3b925a45e 100644 --- a/api_docs/kbn_observability_alert_details.mdx +++ b/api_docs/kbn_observability_alert_details.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alert-details title: "@kbn/observability-alert-details" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alert-details plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alert-details'] --- import kbnObservabilityAlertDetailsObj from './kbn_observability_alert_details.devdocs.json'; diff --git a/api_docs/kbn_observability_alerting_test_data.mdx b/api_docs/kbn_observability_alerting_test_data.mdx index 46db2baac7725..b0feb7bea0422 100644 --- a/api_docs/kbn_observability_alerting_test_data.mdx +++ b/api_docs/kbn_observability_alerting_test_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alerting-test-data title: "@kbn/observability-alerting-test-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alerting-test-data plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alerting-test-data'] --- import kbnObservabilityAlertingTestDataObj from './kbn_observability_alerting_test_data.devdocs.json'; diff --git a/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx b/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx index 6a499920eb57b..0acc2281d3ff6 100644 --- a/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx +++ b/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-get-padded-alert-time-range-util title: "@kbn/observability-get-padded-alert-time-range-util" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-get-padded-alert-time-range-util plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-get-padded-alert-time-range-util'] --- import kbnObservabilityGetPaddedAlertTimeRangeUtilObj from './kbn_observability_get_padded_alert_time_range_util.devdocs.json'; diff --git a/api_docs/kbn_openapi_bundler.mdx b/api_docs/kbn_openapi_bundler.mdx index 893394d5f2309..a64760faad9d0 100644 --- a/api_docs/kbn_openapi_bundler.mdx +++ b/api_docs/kbn_openapi_bundler.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-openapi-bundler title: "@kbn/openapi-bundler" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/openapi-bundler plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/openapi-bundler'] --- import kbnOpenapiBundlerObj from './kbn_openapi_bundler.devdocs.json'; diff --git a/api_docs/kbn_openapi_generator.mdx b/api_docs/kbn_openapi_generator.mdx index 17a3a3363b47f..693f3c620391c 100644 --- a/api_docs/kbn_openapi_generator.mdx +++ b/api_docs/kbn_openapi_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-openapi-generator title: "@kbn/openapi-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/openapi-generator plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/openapi-generator'] --- import kbnOpenapiGeneratorObj from './kbn_openapi_generator.devdocs.json'; diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index 30062861db8aa..0b4676e4d5f19 100644 --- a/api_docs/kbn_optimizer.mdx +++ b/api_docs/kbn_optimizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer title: "@kbn/optimizer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer plugin -date: 2024-05-16 +date: 2024-05-20 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 19e8f1ef457c7..ffc91d2c719a9 100644 --- a/api_docs/kbn_optimizer_webpack_helpers.mdx +++ b/api_docs/kbn_optimizer_webpack_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer-webpack-helpers title: "@kbn/optimizer-webpack-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer-webpack-helpers plugin -date: 2024-05-16 +date: 2024-05-20 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 5988d319eec88..0f64d776b1c11 100644 --- a/api_docs/kbn_osquery_io_ts_types.mdx +++ b/api_docs/kbn_osquery_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-osquery-io-ts-types title: "@kbn/osquery-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/osquery-io-ts-types plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/osquery-io-ts-types'] --- import kbnOsqueryIoTsTypesObj from './kbn_osquery_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_panel_loader.mdx b/api_docs/kbn_panel_loader.mdx index ef342a31fa9e9..d9947f1d9738e 100644 --- a/api_docs/kbn_panel_loader.mdx +++ b/api_docs/kbn_panel_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-panel-loader title: "@kbn/panel-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/panel-loader plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/panel-loader'] --- import kbnPanelLoaderObj from './kbn_panel_loader.devdocs.json'; diff --git a/api_docs/kbn_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index 3115d92cca918..20f1726a4795e 100644 --- a/api_docs/kbn_performance_testing_dataset_extractor.mdx +++ b/api_docs/kbn_performance_testing_dataset_extractor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-performance-testing-dataset-extractor title: "@kbn/performance-testing-dataset-extractor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/performance-testing-dataset-extractor plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/performance-testing-dataset-extractor'] --- import kbnPerformanceTestingDatasetExtractorObj from './kbn_performance_testing_dataset_extractor.devdocs.json'; diff --git a/api_docs/kbn_plugin_check.mdx b/api_docs/kbn_plugin_check.mdx index 70c0cd1635403..8a3b0fe93837d 100644 --- a/api_docs/kbn_plugin_check.mdx +++ b/api_docs/kbn_plugin_check.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-check title: "@kbn/plugin-check" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-check plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-check'] --- import kbnPluginCheckObj from './kbn_plugin_check.devdocs.json'; diff --git a/api_docs/kbn_plugin_generator.mdx b/api_docs/kbn_plugin_generator.mdx index b7272bf02596d..1db3f00e4c792 100644 --- a/api_docs/kbn_plugin_generator.mdx +++ b/api_docs/kbn_plugin_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-generator title: "@kbn/plugin-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-generator plugin -date: 2024-05-16 +date: 2024-05-20 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 2dc3d4b1f5564..bcd1f927c3017 100644 --- a/api_docs/kbn_plugin_helpers.mdx +++ b/api_docs/kbn_plugin_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-helpers title: "@kbn/plugin-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-helpers plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_presentation_containers.mdx b/api_docs/kbn_presentation_containers.mdx index 5c5db58273a13..82745e9533998 100644 --- a/api_docs/kbn_presentation_containers.mdx +++ b/api_docs/kbn_presentation_containers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-presentation-containers title: "@kbn/presentation-containers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/presentation-containers plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/presentation-containers'] --- import kbnPresentationContainersObj from './kbn_presentation_containers.devdocs.json'; diff --git a/api_docs/kbn_presentation_publishing.mdx b/api_docs/kbn_presentation_publishing.mdx index 1d19fc6153c52..1d69b3c6d2a67 100644 --- a/api_docs/kbn_presentation_publishing.mdx +++ b/api_docs/kbn_presentation_publishing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-presentation-publishing title: "@kbn/presentation-publishing" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/presentation-publishing plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/presentation-publishing'] --- import kbnPresentationPublishingObj from './kbn_presentation_publishing.devdocs.json'; diff --git a/api_docs/kbn_profiling_utils.mdx b/api_docs/kbn_profiling_utils.mdx index b3473decf8180..275f30a38fd66 100644 --- a/api_docs/kbn_profiling_utils.mdx +++ b/api_docs/kbn_profiling_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-profiling-utils title: "@kbn/profiling-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/profiling-utils plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/profiling-utils'] --- import kbnProfilingUtilsObj from './kbn_profiling_utils.devdocs.json'; diff --git a/api_docs/kbn_random_sampling.mdx b/api_docs/kbn_random_sampling.mdx index d6dff54af4bfc..fe29e85ae2aa9 100644 --- a/api_docs/kbn_random_sampling.mdx +++ b/api_docs/kbn_random_sampling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-random-sampling title: "@kbn/random-sampling" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/random-sampling plugin -date: 2024-05-16 +date: 2024-05-20 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 b95dab9985f24..e356efd368801 100644 --- a/api_docs/kbn_react_field.mdx +++ b/api_docs/kbn_react_field.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-field title: "@kbn/react-field" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-field plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_react_hooks.mdx b/api_docs/kbn_react_hooks.mdx index d2ff18342228f..99a1838392850 100644 --- a/api_docs/kbn_react_hooks.mdx +++ b/api_docs/kbn_react_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-hooks title: "@kbn/react-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-hooks plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-hooks'] --- import kbnReactHooksObj from './kbn_react_hooks.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_common.mdx b/api_docs/kbn_react_kibana_context_common.mdx index 3d38276562ee3..4188949e59a4b 100644 --- a/api_docs/kbn_react_kibana_context_common.mdx +++ b/api_docs/kbn_react_kibana_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-common title: "@kbn/react-kibana-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-common plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-common'] --- import kbnReactKibanaContextCommonObj from './kbn_react_kibana_context_common.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_render.mdx b/api_docs/kbn_react_kibana_context_render.mdx index 1a02d530677ff..4a873f05cace4 100644 --- a/api_docs/kbn_react_kibana_context_render.mdx +++ b/api_docs/kbn_react_kibana_context_render.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-render title: "@kbn/react-kibana-context-render" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-render plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-render'] --- import kbnReactKibanaContextRenderObj from './kbn_react_kibana_context_render.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_root.mdx b/api_docs/kbn_react_kibana_context_root.mdx index f211ee67186c1..d06e602a384c7 100644 --- a/api_docs/kbn_react_kibana_context_root.mdx +++ b/api_docs/kbn_react_kibana_context_root.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-root title: "@kbn/react-kibana-context-root" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-root plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-root'] --- import kbnReactKibanaContextRootObj from './kbn_react_kibana_context_root.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_styled.mdx b/api_docs/kbn_react_kibana_context_styled.mdx index 939b04a827589..9d153e136ade3 100644 --- a/api_docs/kbn_react_kibana_context_styled.mdx +++ b/api_docs/kbn_react_kibana_context_styled.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-styled title: "@kbn/react-kibana-context-styled" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-styled plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-styled'] --- import kbnReactKibanaContextStyledObj from './kbn_react_kibana_context_styled.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_theme.mdx b/api_docs/kbn_react_kibana_context_theme.mdx index f16b1d5406a9b..b68c77b1d2427 100644 --- a/api_docs/kbn_react_kibana_context_theme.mdx +++ b/api_docs/kbn_react_kibana_context_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-theme title: "@kbn/react-kibana-context-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-theme plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-theme'] --- import kbnReactKibanaContextThemeObj from './kbn_react_kibana_context_theme.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_mount.mdx b/api_docs/kbn_react_kibana_mount.mdx index 8e79c9fa63789..9afddbcc77bf0 100644 --- a/api_docs/kbn_react_kibana_mount.mdx +++ b/api_docs/kbn_react_kibana_mount.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-mount title: "@kbn/react-kibana-mount" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-mount plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-mount'] --- import kbnReactKibanaMountObj from './kbn_react_kibana_mount.devdocs.json'; diff --git a/api_docs/kbn_repo_file_maps.mdx b/api_docs/kbn_repo_file_maps.mdx index 99213d80d4b0c..35f66c28ca036 100644 --- a/api_docs/kbn_repo_file_maps.mdx +++ b/api_docs/kbn_repo_file_maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-file-maps title: "@kbn/repo-file-maps" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-file-maps plugin -date: 2024-05-16 +date: 2024-05-20 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 19a4ffd6a4797..a2315e36827eb 100644 --- a/api_docs/kbn_repo_linter.mdx +++ b/api_docs/kbn_repo_linter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-linter title: "@kbn/repo-linter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-linter plugin -date: 2024-05-16 +date: 2024-05-20 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 f8081532d763c..60709c8d76175 100644 --- a/api_docs/kbn_repo_path.mdx +++ b/api_docs/kbn_repo_path.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-path title: "@kbn/repo-path" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-path plugin -date: 2024-05-16 +date: 2024-05-20 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 01da4c545fd1b..46edbf94b0306 100644 --- a/api_docs/kbn_repo_source_classifier.mdx +++ b/api_docs/kbn_repo_source_classifier.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-source-classifier title: "@kbn/repo-source-classifier" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-source-classifier plugin -date: 2024-05-16 +date: 2024-05-20 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 cb3e30a4c8bdc..6110843c5551b 100644 --- a/api_docs/kbn_reporting_common.mdx +++ b/api_docs/kbn_reporting_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-common title: "@kbn/reporting-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-common plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-common'] --- import kbnReportingCommonObj from './kbn_reporting_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_csv_share_panel.mdx b/api_docs/kbn_reporting_csv_share_panel.mdx index 5f62dc6463774..3e20af5537dbd 100644 --- a/api_docs/kbn_reporting_csv_share_panel.mdx +++ b/api_docs/kbn_reporting_csv_share_panel.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-csv-share-panel title: "@kbn/reporting-csv-share-panel" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-csv-share-panel plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-csv-share-panel'] --- import kbnReportingCsvSharePanelObj from './kbn_reporting_csv_share_panel.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_csv.mdx b/api_docs/kbn_reporting_export_types_csv.mdx index 834913754d3e9..6758cba46782a 100644 --- a/api_docs/kbn_reporting_export_types_csv.mdx +++ b/api_docs/kbn_reporting_export_types_csv.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-csv title: "@kbn/reporting-export-types-csv" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-csv plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-csv'] --- import kbnReportingExportTypesCsvObj from './kbn_reporting_export_types_csv.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_csv_common.mdx b/api_docs/kbn_reporting_export_types_csv_common.mdx index f1993c9dc6974..cbae61c8704be 100644 --- a/api_docs/kbn_reporting_export_types_csv_common.mdx +++ b/api_docs/kbn_reporting_export_types_csv_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-csv-common title: "@kbn/reporting-export-types-csv-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-csv-common plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-csv-common'] --- import kbnReportingExportTypesCsvCommonObj from './kbn_reporting_export_types_csv_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_pdf.mdx b/api_docs/kbn_reporting_export_types_pdf.mdx index 0baedd862b84b..e8bf737e0cf4d 100644 --- a/api_docs/kbn_reporting_export_types_pdf.mdx +++ b/api_docs/kbn_reporting_export_types_pdf.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-pdf title: "@kbn/reporting-export-types-pdf" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-pdf plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-pdf'] --- import kbnReportingExportTypesPdfObj from './kbn_reporting_export_types_pdf.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_pdf_common.mdx b/api_docs/kbn_reporting_export_types_pdf_common.mdx index 55d777ede8542..6fcc103f5a36d 100644 --- a/api_docs/kbn_reporting_export_types_pdf_common.mdx +++ b/api_docs/kbn_reporting_export_types_pdf_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-pdf-common title: "@kbn/reporting-export-types-pdf-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-pdf-common plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-pdf-common'] --- import kbnReportingExportTypesPdfCommonObj from './kbn_reporting_export_types_pdf_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_png.mdx b/api_docs/kbn_reporting_export_types_png.mdx index 72cbe897839b5..0c83de45b0f78 100644 --- a/api_docs/kbn_reporting_export_types_png.mdx +++ b/api_docs/kbn_reporting_export_types_png.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-png title: "@kbn/reporting-export-types-png" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-png plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-png'] --- import kbnReportingExportTypesPngObj from './kbn_reporting_export_types_png.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_png_common.mdx b/api_docs/kbn_reporting_export_types_png_common.mdx index 917278e0379be..e0306685b7dd7 100644 --- a/api_docs/kbn_reporting_export_types_png_common.mdx +++ b/api_docs/kbn_reporting_export_types_png_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-png-common title: "@kbn/reporting-export-types-png-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-png-common plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-png-common'] --- import kbnReportingExportTypesPngCommonObj from './kbn_reporting_export_types_png_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_mocks_server.mdx b/api_docs/kbn_reporting_mocks_server.mdx index f4c24fbe6e150..31fc719ea844e 100644 --- a/api_docs/kbn_reporting_mocks_server.mdx +++ b/api_docs/kbn_reporting_mocks_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-mocks-server title: "@kbn/reporting-mocks-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-mocks-server plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-mocks-server'] --- import kbnReportingMocksServerObj from './kbn_reporting_mocks_server.devdocs.json'; diff --git a/api_docs/kbn_reporting_public.mdx b/api_docs/kbn_reporting_public.mdx index 4271e03b4adbd..db9e3868a9098 100644 --- a/api_docs/kbn_reporting_public.mdx +++ b/api_docs/kbn_reporting_public.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-public title: "@kbn/reporting-public" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-public plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-public'] --- import kbnReportingPublicObj from './kbn_reporting_public.devdocs.json'; diff --git a/api_docs/kbn_reporting_server.mdx b/api_docs/kbn_reporting_server.mdx index 5b1f1cbb75d4b..057e3b4216576 100644 --- a/api_docs/kbn_reporting_server.mdx +++ b/api_docs/kbn_reporting_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-server title: "@kbn/reporting-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-server plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-server'] --- import kbnReportingServerObj from './kbn_reporting_server.devdocs.json'; diff --git a/api_docs/kbn_resizable_layout.mdx b/api_docs/kbn_resizable_layout.mdx index 697ee4e73a921..75ff9919b5216 100644 --- a/api_docs/kbn_resizable_layout.mdx +++ b/api_docs/kbn_resizable_layout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-resizable-layout title: "@kbn/resizable-layout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/resizable-layout plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/resizable-layout'] --- import kbnResizableLayoutObj from './kbn_resizable_layout.devdocs.json'; diff --git a/api_docs/kbn_rison.mdx b/api_docs/kbn_rison.mdx index f9b9e50825e7f..7593302c72d33 100644 --- a/api_docs/kbn_rison.mdx +++ b/api_docs/kbn_rison.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rison title: "@kbn/rison" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rison plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rison'] --- import kbnRisonObj from './kbn_rison.devdocs.json'; diff --git a/api_docs/kbn_router_to_openapispec.mdx b/api_docs/kbn_router_to_openapispec.mdx index 1b70f82179fcf..a148497154005 100644 --- a/api_docs/kbn_router_to_openapispec.mdx +++ b/api_docs/kbn_router_to_openapispec.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-router-to-openapispec title: "@kbn/router-to-openapispec" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/router-to-openapispec plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/router-to-openapispec'] --- import kbnRouterToOpenapispecObj from './kbn_router_to_openapispec.devdocs.json'; diff --git a/api_docs/kbn_router_utils.mdx b/api_docs/kbn_router_utils.mdx index 3ac83c9da4088..00624f80bdc87 100644 --- a/api_docs/kbn_router_utils.mdx +++ b/api_docs/kbn_router_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-router-utils title: "@kbn/router-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/router-utils plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/router-utils'] --- import kbnRouterUtilsObj from './kbn_router_utils.devdocs.json'; diff --git a/api_docs/kbn_rrule.mdx b/api_docs/kbn_rrule.mdx index 65435c955102a..434774814e5e1 100644 --- a/api_docs/kbn_rrule.mdx +++ b/api_docs/kbn_rrule.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rrule title: "@kbn/rrule" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rrule plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rrule'] --- import kbnRruleObj from './kbn_rrule.devdocs.json'; diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index fc45e0583c934..660759341c092 100644 --- a/api_docs/kbn_rule_data_utils.mdx +++ b/api_docs/kbn_rule_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rule-data-utils title: "@kbn/rule-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rule-data-utils plugin -date: 2024-05-16 +date: 2024-05-20 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 3a04b271ffa77..885459bb55477 100644 --- a/api_docs/kbn_saved_objects_settings.mdx +++ b/api_docs/kbn_saved_objects_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-saved-objects-settings title: "@kbn/saved-objects-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/saved-objects-settings plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/saved-objects-settings'] --- import kbnSavedObjectsSettingsObj from './kbn_saved_objects_settings.devdocs.json'; diff --git a/api_docs/kbn_search_api_panels.devdocs.json b/api_docs/kbn_search_api_panels.devdocs.json index 6b64604da0fec..3e17ad34f9985 100644 --- a/api_docs/kbn_search_api_panels.devdocs.json +++ b/api_docs/kbn_search_api_panels.devdocs.json @@ -326,7 +326,7 @@ "label": "IngestData", "description": [], "signature": [ - "({ codeSnippet, selectedLanguage, setSelectedLanguage, docLinks, assetBasePath, application, consolePlugin, sharePlugin, languages, consoleRequest, additionalIngestionPanel, }: React.PropsWithChildren) => JSX.Element" + "({ codeSnippet, selectedLanguage, selectedPipeline, setSelectedLanguage, docLinks, assetBasePath, application, consolePlugin, sharePlugin, languages, consoleRequest, additionalIngestionPanel, ingestPipelineData, setSelectedPipeline, defaultIngestPipeline, }: React.PropsWithChildren) => JSX.Element" ], "path": "packages/kbn-search-api-panels/components/ingest_data.tsx", "deprecated": false, @@ -337,7 +337,7 @@ "id": "def-common.IngestData.$1", "type": "CompoundType", "tags": [], - "label": "{\n codeSnippet,\n selectedLanguage,\n setSelectedLanguage,\n docLinks,\n assetBasePath,\n application,\n consolePlugin,\n sharePlugin,\n languages,\n consoleRequest,\n additionalIngestionPanel,\n}", + "label": "{\n codeSnippet,\n selectedLanguage,\n selectedPipeline,\n setSelectedLanguage,\n docLinks,\n assetBasePath,\n application,\n consolePlugin,\n sharePlugin,\n languages,\n consoleRequest,\n additionalIngestionPanel,\n ingestPipelineData,\n setSelectedPipeline,\n defaultIngestPipeline,\n}", "description": [], "signature": [ "React.PropsWithChildren" @@ -384,6 +384,39 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/search-api-panels", + "id": "def-common.IngestPipelinePanel", + "type": "Function", + "tags": [], + "label": "IngestPipelinePanel", + "description": [], + "signature": [ + "({ selectedPipeline, setSelectedPipeline, ingestPipelinesData, defaultIngestPipeline, }: React.PropsWithChildren) => JSX.Element" + ], + "path": "packages/kbn-search-api-panels/components/ingest_pipelines/ingest_pipeline_panel.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-api-panels", + "id": "def-common.IngestPipelinePanel.$1", + "type": "CompoundType", + "tags": [], + "label": "{\n selectedPipeline,\n setSelectedPipeline,\n ingestPipelinesData,\n defaultIngestPipeline,\n}", + "description": [], + "signature": [ + "React.PropsWithChildren" + ], + "path": "packages/kbn-search-api-panels/components/ingest_pipelines/ingest_pipeline_panel.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/search-api-panels", "id": "def-common.InstallClientPanel", diff --git a/api_docs/kbn_search_api_panels.mdx b/api_docs/kbn_search_api_panels.mdx index 0761b37a04863..d83b26f6fc708 100644 --- a/api_docs/kbn_search_api_panels.mdx +++ b/api_docs/kbn_search_api_panels.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-api-panels title: "@kbn/search-api-panels" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-api-panels plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-api-panels'] --- import kbnSearchApiPanelsObj from './kbn_search_api_panels.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/te | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 74 | 0 | 74 | 0 | +| 76 | 0 | 76 | 0 | ## Common diff --git a/api_docs/kbn_search_connectors.devdocs.json b/api_docs/kbn_search_connectors.devdocs.json index 1536cc0f405c5..703be6afbdcd4 100644 --- a/api_docs/kbn_search_connectors.devdocs.json +++ b/api_docs/kbn_search_connectors.devdocs.json @@ -2643,15 +2643,7 @@ "section": "def-common.ElasticsearchClient", "text": "ElasticsearchClient" }, - ", connectorId: string, { advancedSnippet, filteringRules, }: { advancedSnippet: string; filteringRules: ", - { - "pluginId": "@kbn/search-connectors", - "scope": "common", - "docId": "kibKbnSearchConnectorsPluginApi", - "section": "def-common.FilteringRule", - "text": "FilteringRule" - }, - "[]; }) => Promise<", + ", connectorId: string) => Promise<", { "pluginId": "@kbn/search-connectors", "scope": "common", @@ -2700,51 +2692,6 @@ "deprecated": false, "trackAdoption": false, "isRequired": true - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.updateFiltering.$3", - "type": "Object", - "tags": [], - "label": "{\n advancedSnippet,\n filteringRules,\n }", - "description": [], - "path": "packages/kbn-search-connectors/lib/update_filtering.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.updateFiltering.$3.advancedSnippet", - "type": "string", - "tags": [], - "label": "advancedSnippet", - "description": [], - "path": "packages/kbn-search-connectors/lib/update_filtering.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.updateFiltering.$3.filteringRules", - "type": "Array", - "tags": [], - "label": "filteringRules", - "description": [], - "signature": [ - { - "pluginId": "@kbn/search-connectors", - "scope": "common", - "docId": "kibKbnSearchConnectorsPluginApi", - "section": "def-common.FilteringRule", - "text": "FilteringRule" - }, - "[]" - ], - "path": "packages/kbn-search-connectors/lib/update_filtering.ts", - "deprecated": false, - "trackAdoption": false - } - ] } ], "returnComment": [], @@ -45942,6 +45889,206 @@ } ] }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_document_level_security", + "type": "Object", + "tags": [], + "label": "use_document_level_security", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_document_level_security.default_value", + "type": "Uncategorized", + "tags": [], + "label": "default_value", + "description": [], + "signature": [ + "null" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_document_level_security.depends_on", + "type": "Array", + "tags": [], + "label": "depends_on", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_document_level_security.display", + "type": "string", + "tags": [], + "label": "display", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.DisplayType", + "text": "DisplayType" + }, + ".TOGGLE" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_document_level_security.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_document_level_security.options", + "type": "Array", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_document_level_security.order", + "type": "number", + "tags": [], + "label": "order", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_document_level_security.required", + "type": "boolean", + "tags": [], + "label": "required", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_document_level_security.sensitive", + "type": "boolean", + "tags": [], + "label": "sensitive", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_document_level_security.tooltip", + "type": "string", + "tags": [], + "label": "tooltip", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_document_level_security.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.FieldType", + "text": "FieldType" + }, + ".BOOLEAN" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_document_level_security.ui_restrictions", + "type": "Array", + "tags": [], + "label": "ui_restrictions", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_document_level_security.validations", + "type": "Array", + "tags": [], + "label": "validations", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_document_level_security.value", + "type": "boolean", + "tags": [], + "label": "value", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, { "parentPluginId": "@kbn/search-connectors", "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_text_extraction_service", diff --git a/api_docs/kbn_search_connectors.mdx b/api_docs/kbn_search_connectors.mdx index 9e5db9a384896..1eda416fa3a51 100644 --- a/api_docs/kbn_search_connectors.mdx +++ b/api_docs/kbn_search_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-connectors title: "@kbn/search-connectors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-connectors plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-connectors'] --- import kbnSearchConnectorsObj from './kbn_search_connectors.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/te | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3661 | 0 | 3661 | 0 | +| 3672 | 0 | 3672 | 0 | ## Common diff --git a/api_docs/kbn_search_errors.mdx b/api_docs/kbn_search_errors.mdx index bfef1bd8b3aea..cf687aaae3b14 100644 --- a/api_docs/kbn_search_errors.mdx +++ b/api_docs/kbn_search_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-errors title: "@kbn/search-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-errors plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-errors'] --- import kbnSearchErrorsObj from './kbn_search_errors.devdocs.json'; diff --git a/api_docs/kbn_search_index_documents.mdx b/api_docs/kbn_search_index_documents.mdx index 984eabeb4f418..343eb75e8bcc3 100644 --- a/api_docs/kbn_search_index_documents.mdx +++ b/api_docs/kbn_search_index_documents.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-index-documents title: "@kbn/search-index-documents" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-index-documents plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-index-documents'] --- import kbnSearchIndexDocumentsObj from './kbn_search_index_documents.devdocs.json'; diff --git a/api_docs/kbn_search_response_warnings.mdx b/api_docs/kbn_search_response_warnings.mdx index 490d1081a6e8a..8166b5a31026a 100644 --- a/api_docs/kbn_search_response_warnings.mdx +++ b/api_docs/kbn_search_response_warnings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-response-warnings title: "@kbn/search-response-warnings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-response-warnings plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-response-warnings'] --- import kbnSearchResponseWarningsObj from './kbn_search_response_warnings.devdocs.json'; diff --git a/api_docs/kbn_search_types.mdx b/api_docs/kbn_search_types.mdx index 387775730d6cf..88ba7c3a0a52c 100644 --- a/api_docs/kbn_search_types.mdx +++ b/api_docs/kbn_search_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-types title: "@kbn/search-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-types plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-types'] --- import kbnSearchTypesObj from './kbn_search_types.devdocs.json'; diff --git a/api_docs/kbn_security_hardening.mdx b/api_docs/kbn_security_hardening.mdx index 1211f3deb9f7f..519bec9afc817 100644 --- a/api_docs/kbn_security_hardening.mdx +++ b/api_docs/kbn_security_hardening.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-hardening title: "@kbn/security-hardening" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-hardening plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-hardening'] --- import kbnSecurityHardeningObj from './kbn_security_hardening.devdocs.json'; diff --git a/api_docs/kbn_security_plugin_types_common.mdx b/api_docs/kbn_security_plugin_types_common.mdx index 1fae3b2181779..bf526f1d45a54 100644 --- a/api_docs/kbn_security_plugin_types_common.mdx +++ b/api_docs/kbn_security_plugin_types_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-plugin-types-common title: "@kbn/security-plugin-types-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-plugin-types-common plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-plugin-types-common'] --- import kbnSecurityPluginTypesCommonObj from './kbn_security_plugin_types_common.devdocs.json'; diff --git a/api_docs/kbn_security_plugin_types_public.mdx b/api_docs/kbn_security_plugin_types_public.mdx index f7e6285fa9233..f32b2f136dab7 100644 --- a/api_docs/kbn_security_plugin_types_public.mdx +++ b/api_docs/kbn_security_plugin_types_public.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-plugin-types-public title: "@kbn/security-plugin-types-public" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-plugin-types-public plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-plugin-types-public'] --- import kbnSecurityPluginTypesPublicObj from './kbn_security_plugin_types_public.devdocs.json'; diff --git a/api_docs/kbn_security_plugin_types_server.mdx b/api_docs/kbn_security_plugin_types_server.mdx index c3098606794d2..068253b884d66 100644 --- a/api_docs/kbn_security_plugin_types_server.mdx +++ b/api_docs/kbn_security_plugin_types_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-plugin-types-server title: "@kbn/security-plugin-types-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-plugin-types-server plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-plugin-types-server'] --- import kbnSecurityPluginTypesServerObj from './kbn_security_plugin_types_server.devdocs.json'; diff --git a/api_docs/kbn_security_solution_features.mdx b/api_docs/kbn_security_solution_features.mdx index d119d9d2c72d5..7e68576b0b1d9 100644 --- a/api_docs/kbn_security_solution_features.mdx +++ b/api_docs/kbn_security_solution_features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-features title: "@kbn/security-solution-features" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-features plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-features'] --- import kbnSecuritySolutionFeaturesObj from './kbn_security_solution_features.devdocs.json'; diff --git a/api_docs/kbn_security_solution_navigation.mdx b/api_docs/kbn_security_solution_navigation.mdx index 21a0574be4cf1..d706a7302fe4b 100644 --- a/api_docs/kbn_security_solution_navigation.mdx +++ b/api_docs/kbn_security_solution_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-navigation title: "@kbn/security-solution-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-navigation plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-navigation'] --- import kbnSecuritySolutionNavigationObj from './kbn_security_solution_navigation.devdocs.json'; diff --git a/api_docs/kbn_security_solution_side_nav.mdx b/api_docs/kbn_security_solution_side_nav.mdx index 63fdbc46a818b..a72a4ccc8522c 100644 --- a/api_docs/kbn_security_solution_side_nav.mdx +++ b/api_docs/kbn_security_solution_side_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-side-nav title: "@kbn/security-solution-side-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-side-nav plugin -date: 2024-05-16 +date: 2024-05-20 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 10fedf5ce7e76..7b2b9cd6e792f 100644 --- a/api_docs/kbn_security_solution_storybook_config.mdx +++ b/api_docs/kbn_security_solution_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-storybook-config title: "@kbn/security-solution-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-storybook-config plugin -date: 2024-05-16 +date: 2024-05-20 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 4702520018bdd..ed2d948903f0c 100644 --- a/api_docs/kbn_securitysolution_autocomplete.mdx +++ b/api_docs/kbn_securitysolution_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-autocomplete title: "@kbn/securitysolution-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-autocomplete plugin -date: 2024-05-16 +date: 2024-05-20 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 ebc9a026ea74f..562c5faaf5a03 100644 --- a/api_docs/kbn_securitysolution_data_table.mdx +++ b/api_docs/kbn_securitysolution_data_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-data-table title: "@kbn/securitysolution-data-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-data-table plugin -date: 2024-05-16 +date: 2024-05-20 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 0f486cabed185..c550f218f43e8 100644 --- a/api_docs/kbn_securitysolution_ecs.mdx +++ b/api_docs/kbn_securitysolution_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-ecs title: "@kbn/securitysolution-ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-ecs plugin -date: 2024-05-16 +date: 2024-05-20 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 4506b13a9fa35..75db667e949f4 100644 --- a/api_docs/kbn_securitysolution_es_utils.mdx +++ b/api_docs/kbn_securitysolution_es_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-es-utils title: "@kbn/securitysolution-es-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-es-utils plugin -date: 2024-05-16 +date: 2024-05-20 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 dd3be276a74ce..e9a6389ff2b3f 100644 --- a/api_docs/kbn_securitysolution_exception_list_components.mdx +++ b/api_docs/kbn_securitysolution_exception_list_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-exception-list-components title: "@kbn/securitysolution-exception-list-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-exception-list-components plugin -date: 2024-05-16 +date: 2024-05-20 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 83a4fa2286df8..d8e4fcbf3ddde 100644 --- a/api_docs/kbn_securitysolution_grouping.mdx +++ b/api_docs/kbn_securitysolution_grouping.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-grouping title: "@kbn/securitysolution-grouping" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-grouping plugin -date: 2024-05-16 +date: 2024-05-20 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 f21facb7dacf1..ab9bc1108f5d2 100644 --- a/api_docs/kbn_securitysolution_hook_utils.mdx +++ b/api_docs/kbn_securitysolution_hook_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-hook-utils title: "@kbn/securitysolution-hook-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-hook-utils plugin -date: 2024-05-16 +date: 2024-05-20 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 baae394990f71..1b3a7c22fecd5 100644 --- a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-alerting-types title: "@kbn/securitysolution-io-ts-alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-alerting-types plugin -date: 2024-05-16 +date: 2024-05-20 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 4142401c57e43..c572a1ce3de72 100644 --- a/api_docs/kbn_securitysolution_io_ts_list_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_list_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-list-types title: "@kbn/securitysolution-io-ts-list-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-list-types plugin -date: 2024-05-16 +date: 2024-05-20 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 9440ca4903a74..a2e076c65d1fb 100644 --- a/api_docs/kbn_securitysolution_io_ts_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-types title: "@kbn/securitysolution-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-types plugin -date: 2024-05-16 +date: 2024-05-20 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 56497662d301e..e66309cab986b 100644 --- a/api_docs/kbn_securitysolution_io_ts_utils.mdx +++ b/api_docs/kbn_securitysolution_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-utils title: "@kbn/securitysolution-io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-utils plugin -date: 2024-05-16 +date: 2024-05-20 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 5f7eff5937ee5..35a57a3d6078a 100644 --- a/api_docs/kbn_securitysolution_list_api.mdx +++ b/api_docs/kbn_securitysolution_list_api.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-api title: "@kbn/securitysolution-list-api" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-api plugin -date: 2024-05-16 +date: 2024-05-20 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 fd2bbf80c087b..63afb89f8fee0 100644 --- a/api_docs/kbn_securitysolution_list_constants.mdx +++ b/api_docs/kbn_securitysolution_list_constants.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-constants title: "@kbn/securitysolution-list-constants" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-constants plugin -date: 2024-05-16 +date: 2024-05-20 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 f1004ec7b35de..4ea6135b018d4 100644 --- a/api_docs/kbn_securitysolution_list_hooks.mdx +++ b/api_docs/kbn_securitysolution_list_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-hooks title: "@kbn/securitysolution-list-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-hooks plugin -date: 2024-05-16 +date: 2024-05-20 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 bdfa6b9b967ab..5a9ad6150157a 100644 --- a/api_docs/kbn_securitysolution_list_utils.mdx +++ b/api_docs/kbn_securitysolution_list_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-utils title: "@kbn/securitysolution-list-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-utils plugin -date: 2024-05-16 +date: 2024-05-20 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 62ac97e20720e..8cf96bfc154bd 100644 --- a/api_docs/kbn_securitysolution_rules.mdx +++ b/api_docs/kbn_securitysolution_rules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-rules title: "@kbn/securitysolution-rules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-rules plugin -date: 2024-05-16 +date: 2024-05-20 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 a019393fea119..63c046237caf1 100644 --- a/api_docs/kbn_securitysolution_t_grid.mdx +++ b/api_docs/kbn_securitysolution_t_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-t-grid title: "@kbn/securitysolution-t-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-t-grid plugin -date: 2024-05-16 +date: 2024-05-20 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 1735a03457eb9..ca6f7abd6e5b5 100644 --- a/api_docs/kbn_securitysolution_utils.mdx +++ b/api_docs/kbn_securitysolution_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-utils title: "@kbn/securitysolution-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-utils plugin -date: 2024-05-16 +date: 2024-05-20 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 262e529fa13aa..f34a671c1a48c 100644 --- a/api_docs/kbn_server_http_tools.mdx +++ b/api_docs/kbn_server_http_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-http-tools title: "@kbn/server-http-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-http-tools plugin -date: 2024-05-16 +date: 2024-05-20 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 1db39cadbf035..01e8f7843532e 100644 --- a/api_docs/kbn_server_route_repository.mdx +++ b/api_docs/kbn_server_route_repository.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository title: "@kbn/server-route-repository" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-route-repository plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_serverless_common_settings.mdx b/api_docs/kbn_serverless_common_settings.mdx index 22a9f74217185..745260e4a94f8 100644 --- a/api_docs/kbn_serverless_common_settings.mdx +++ b/api_docs/kbn_serverless_common_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-common-settings title: "@kbn/serverless-common-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-common-settings plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-common-settings'] --- import kbnServerlessCommonSettingsObj from './kbn_serverless_common_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_observability_settings.mdx b/api_docs/kbn_serverless_observability_settings.mdx index e977a2820cdb7..d9c17089ddc89 100644 --- a/api_docs/kbn_serverless_observability_settings.mdx +++ b/api_docs/kbn_serverless_observability_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-observability-settings title: "@kbn/serverless-observability-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-observability-settings plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-observability-settings'] --- import kbnServerlessObservabilitySettingsObj from './kbn_serverless_observability_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_project_switcher.mdx b/api_docs/kbn_serverless_project_switcher.mdx index efaaa4e4114f6..a5b47a9080ee1 100644 --- a/api_docs/kbn_serverless_project_switcher.mdx +++ b/api_docs/kbn_serverless_project_switcher.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-project-switcher title: "@kbn/serverless-project-switcher" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-project-switcher plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-project-switcher'] --- import kbnServerlessProjectSwitcherObj from './kbn_serverless_project_switcher.devdocs.json'; diff --git a/api_docs/kbn_serverless_search_settings.mdx b/api_docs/kbn_serverless_search_settings.mdx index 9a854220887bf..b89a43151117a 100644 --- a/api_docs/kbn_serverless_search_settings.mdx +++ b/api_docs/kbn_serverless_search_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-search-settings title: "@kbn/serverless-search-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-search-settings plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-search-settings'] --- import kbnServerlessSearchSettingsObj from './kbn_serverless_search_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_security_settings.mdx b/api_docs/kbn_serverless_security_settings.mdx index 107e89971e6f6..4b79a918b03e8 100644 --- a/api_docs/kbn_serverless_security_settings.mdx +++ b/api_docs/kbn_serverless_security_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-security-settings title: "@kbn/serverless-security-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-security-settings plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-security-settings'] --- import kbnServerlessSecuritySettingsObj from './kbn_serverless_security_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_storybook_config.mdx b/api_docs/kbn_serverless_storybook_config.mdx index dca21675ec13b..d239e446791a4 100644 --- a/api_docs/kbn_serverless_storybook_config.mdx +++ b/api_docs/kbn_serverless_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-storybook-config title: "@kbn/serverless-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-storybook-config plugin -date: 2024-05-16 +date: 2024-05-20 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 98702aa2c1804..0659128adf42d 100644 --- a/api_docs/kbn_shared_svg.mdx +++ b/api_docs/kbn_shared_svg.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-svg title: "@kbn/shared-svg" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-svg plugin -date: 2024-05-16 +date: 2024-05-20 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 3b36ea5d1e9c0..21f93739bcc4e 100644 --- a/api_docs/kbn_shared_ux_avatar_solution.mdx +++ b/api_docs/kbn_shared_ux_avatar_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-solution title: "@kbn/shared-ux-avatar-solution" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-solution plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-solution'] --- import kbnSharedUxAvatarSolutionObj from './kbn_shared_ux_avatar_solution.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx index a518de989cb57..62a7052d3e130 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen title: "@kbn/shared-ux-button-exit-full-screen" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen'] --- import kbnSharedUxButtonExitFullScreenObj from './kbn_shared_ux_button_exit_full_screen.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_toolbar.mdx b/api_docs/kbn_shared_ux_button_toolbar.mdx index 1bc4f54b8fa0e..8bc05e22be7ef 100644 --- a/api_docs/kbn_shared_ux_button_toolbar.mdx +++ b/api_docs/kbn_shared_ux_button_toolbar.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-toolbar title: "@kbn/shared-ux-button-toolbar" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-toolbar plugin -date: 2024-05-16 +date: 2024-05-20 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 8361a23eb2903..153598bbb2a55 100644 --- a/api_docs/kbn_shared_ux_card_no_data.mdx +++ b/api_docs/kbn_shared_ux_card_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data title: "@kbn/shared-ux-card-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data plugin -date: 2024-05-16 +date: 2024-05-20 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 35deb9618afa1..f819a384e2403 100644 --- a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data-mocks title: "@kbn/shared-ux-card-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 f907c556f1c87..1618dea67537d 100644 --- a/api_docs/kbn_shared_ux_chrome_navigation.mdx +++ b/api_docs/kbn_shared_ux_chrome_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-chrome-navigation title: "@kbn/shared-ux-chrome-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-chrome-navigation plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-chrome-navigation'] --- import kbnSharedUxChromeNavigationObj from './kbn_shared_ux_chrome_navigation.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_error_boundary.mdx b/api_docs/kbn_shared_ux_error_boundary.mdx index 4d2f5f1823e80..d265a68b65e4c 100644 --- a/api_docs/kbn_shared_ux_error_boundary.mdx +++ b/api_docs/kbn_shared_ux_error_boundary.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-error-boundary title: "@kbn/shared-ux-error-boundary" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-error-boundary plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-error-boundary'] --- import kbnSharedUxErrorBoundaryObj from './kbn_shared_ux_error_boundary.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_context.mdx b/api_docs/kbn_shared_ux_file_context.mdx index f645cd4d00c9e..18f0b044001b1 100644 --- a/api_docs/kbn_shared_ux_file_context.mdx +++ b/api_docs/kbn_shared_ux_file_context.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-context title: "@kbn/shared-ux-file-context" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-context plugin -date: 2024-05-16 +date: 2024-05-20 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 1254ca2154b94..b2fedf50c82d0 100644 --- a/api_docs/kbn_shared_ux_file_image.mdx +++ b/api_docs/kbn_shared_ux_file_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image title: "@kbn/shared-ux-file-image" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image plugin -date: 2024-05-16 +date: 2024-05-20 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 a338ea52665e2..6ffe0d1b1930f 100644 --- a/api_docs/kbn_shared_ux_file_image_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_image_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image-mocks title: "@kbn/shared-ux-file-image-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 8ef215892bacc..2fdf7b0760c59 100644 --- a/api_docs/kbn_shared_ux_file_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-mocks title: "@kbn/shared-ux-file-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 45c20f1b23428..682990dc1a483 100644 --- a/api_docs/kbn_shared_ux_file_picker.mdx +++ b/api_docs/kbn_shared_ux_file_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-picker title: "@kbn/shared-ux-file-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-picker plugin -date: 2024-05-16 +date: 2024-05-20 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 d0965a9c5c3a0..6e66f71e5d71b 100644 --- a/api_docs/kbn_shared_ux_file_types.mdx +++ b/api_docs/kbn_shared_ux_file_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-types title: "@kbn/shared-ux-file-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-types plugin -date: 2024-05-16 +date: 2024-05-20 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 e7521ef97563c..32692d3f3b59e 100644 --- a/api_docs/kbn_shared_ux_file_upload.mdx +++ b/api_docs/kbn_shared_ux_file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-upload title: "@kbn/shared-ux-file-upload" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-upload plugin -date: 2024-05-16 +date: 2024-05-20 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 2d5cea3cf4b74..a1c70d25a2527 100644 --- a/api_docs/kbn_shared_ux_file_util.mdx +++ b/api_docs/kbn_shared_ux_file_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-util title: "@kbn/shared-ux-file-util" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-util plugin -date: 2024-05-16 +date: 2024-05-20 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 ebce9336e18f5..591bb419eef5a 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app title: "@kbn/shared-ux-link-redirect-app" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app plugin -date: 2024-05-16 +date: 2024-05-20 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 540063b47b078..c4ad07d9702e6 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app-mocks title: "@kbn/shared-ux-link-redirect-app-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 0094b649ab73c..27828e3a97c76 100644 --- a/api_docs/kbn_shared_ux_markdown.mdx +++ b/api_docs/kbn_shared_ux_markdown.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown title: "@kbn/shared-ux-markdown" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown plugin -date: 2024-05-16 +date: 2024-05-20 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 549075b984ec6..824cd38f2b0b1 100644 --- a/api_docs/kbn_shared_ux_markdown_mocks.mdx +++ b/api_docs/kbn_shared_ux_markdown_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown-mocks title: "@kbn/shared-ux-markdown-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 b2259a25a7042..58368a6162f8d 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data title: "@kbn/shared-ux-page-analytics-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data plugin -date: 2024-05-16 +date: 2024-05-20 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 233e5b4be552c..14c974ba11a34 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data-mocks title: "@kbn/shared-ux-page-analytics-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 b9b28030ef322..87eb0d4ae5c7b 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data title: "@kbn/shared-ux-page-kibana-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data plugin -date: 2024-05-16 +date: 2024-05-20 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 f7c4adf7e8081..9d8a19a7c71c7 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data-mocks title: "@kbn/shared-ux-page-kibana-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 26e2bd19edceb..e48dcb987f647 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template title: "@kbn/shared-ux-page-kibana-template" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template plugin -date: 2024-05-16 +date: 2024-05-20 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 1c336dd3a7fe5..567f2431b390c 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template-mocks title: "@kbn/shared-ux-page-kibana-template-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 08ef89efcbd47..8887812c45027 100644 --- a/api_docs/kbn_shared_ux_page_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data title: "@kbn/shared-ux-page-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data plugin -date: 2024-05-16 +date: 2024-05-20 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 09c1fc506e3c6..b7fd2434c980b 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config title: "@kbn/shared-ux-page-no-data-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config plugin -date: 2024-05-16 +date: 2024-05-20 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 8f07624c61f94..22946e98f94b9 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config-mocks title: "@kbn/shared-ux-page-no-data-config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 ef5ce2e346ee5..3732f6e44771d 100644 --- a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-mocks title: "@kbn/shared-ux-page-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 e11424c8b9a73..67406176211cd 100644 --- a/api_docs/kbn_shared_ux_page_solution_nav.mdx +++ b/api_docs/kbn_shared_ux_page_solution_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-solution-nav title: "@kbn/shared-ux-page-solution-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-solution-nav plugin -date: 2024-05-16 +date: 2024-05-20 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 d6bcbca90ae50..919798b6b1de3 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views title: "@kbn/shared-ux-prompt-no-data-views" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views plugin -date: 2024-05-16 +date: 2024-05-20 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 b01e77817da3c..7394c69d76951 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views-mocks title: "@kbn/shared-ux-prompt-no-data-views-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 42477fc361f44..f9d71f1f23424 100644 --- a/api_docs/kbn_shared_ux_prompt_not_found.mdx +++ b/api_docs/kbn_shared_ux_prompt_not_found.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-not-found title: "@kbn/shared-ux-prompt-not-found" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-not-found plugin -date: 2024-05-16 +date: 2024-05-20 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 600f2c8ef0234..59886686b30d6 100644 --- a/api_docs/kbn_shared_ux_router.mdx +++ b/api_docs/kbn_shared_ux_router.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router title: "@kbn/shared-ux-router" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router plugin -date: 2024-05-16 +date: 2024-05-20 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 579d9be1c6ffe..9def918de0020 100644 --- a/api_docs/kbn_shared_ux_router_mocks.mdx +++ b/api_docs/kbn_shared_ux_router_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router-mocks title: "@kbn/shared-ux-router-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router-mocks plugin -date: 2024-05-16 +date: 2024-05-20 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 54a7bd5123d50..041e9720f7984 100644 --- a/api_docs/kbn_shared_ux_storybook_config.mdx +++ b/api_docs/kbn_shared_ux_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-config title: "@kbn/shared-ux-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-config plugin -date: 2024-05-16 +date: 2024-05-20 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 c1b20a9cc7c2a..d635489810956 100644 --- a/api_docs/kbn_shared_ux_storybook_mock.mdx +++ b/api_docs/kbn_shared_ux_storybook_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-mock title: "@kbn/shared-ux-storybook-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-mock plugin -date: 2024-05-16 +date: 2024-05-20 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_tabbed_modal.mdx b/api_docs/kbn_shared_ux_tabbed_modal.mdx index 6eb0d20daa312..c92a246c73fd5 100644 --- a/api_docs/kbn_shared_ux_tabbed_modal.mdx +++ b/api_docs/kbn_shared_ux_tabbed_modal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-tabbed-modal title: "@kbn/shared-ux-tabbed-modal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-tabbed-modal plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-tabbed-modal'] --- import kbnSharedUxTabbedModalObj from './kbn_shared_ux_tabbed_modal.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index 3fc67c924f5ab..37fd11222f5f7 100644 --- a/api_docs/kbn_shared_ux_utility.mdx +++ b/api_docs/kbn_shared_ux_utility.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-utility title: "@kbn/shared-ux-utility" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-utility plugin -date: 2024-05-16 +date: 2024-05-20 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.devdocs.json b/api_docs/kbn_slo_schema.devdocs.json index 2322c73758327..fb749318c15e2 100644 --- a/api_docs/kbn_slo_schema.devdocs.json +++ b/api_docs/kbn_slo_schema.devdocs.json @@ -745,7 +745,7 @@ "section": "def-common.Duration", "text": "Duration" }, - " | undefined; }; groupBy: string | string[]; revision: number; } & { remoteName?: string | undefined; range?: { from: string; to: string; } | undefined; })[]; }" + " | undefined; }; groupBy: string | string[]; revision: number; } & { remoteName?: string | undefined; range?: { from: Date; to: Date; } | undefined; })[]; }" ], "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/routes/fetch_historical_summary.ts", "deprecated": false, @@ -910,7 +910,7 @@ "label": "GetPreviewDataParams", "description": [], "signature": [ - "{ indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"last_value\" | \"cardinality\" | \"std_deviation\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }; }; range: { start: number; end: number; }; } & { objective?: ({ target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: ", + "{ indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"last_value\" | \"cardinality\" | \"std_deviation\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }; }; range: { from: Date; to: Date; }; } & { objective?: ({ target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: ", { "pluginId": "@kbn/slo-schema", "scope": "common", @@ -3241,18 +3241,10 @@ "signature": [ "TypeC", "<{ from: ", - "UnionC", - "<[", "Type", - ", ", - "StringC", - "]>; to: ", - "UnionC", - "<[", + "; to: ", "Type", - ", ", - "StringC", - "]>; }>" + "; }>" ], "path": "x-pack/packages/kbn-slo-schema/src/schema/common.ts", "deprecated": false, @@ -3475,10 +3467,10 @@ "; range: ", "TypeC", "<{ from: ", - "StringC", - "; to: ", - "StringC", - "; }>; }>]>>; }>; }>" + "Type", + "; to: ", + "Type", + "; }>; }>]>>; }>; }>" ], "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/routes/fetch_historical_summary.ts", "deprecated": false, @@ -8054,11 +8046,11 @@ "AnyC", ">; }>>; }>]>; }>]>; }>]>; range: ", "TypeC", - "<{ start: ", - "NumberC", - "; end: ", - "NumberC", - "; }>; }>, ", + "<{ from: ", + "Type", + "; to: ", + "Type", + "; }>; }>, ", "PartialC", "<{ objective: ", "IntersectionC", diff --git a/api_docs/kbn_slo_schema.mdx b/api_docs/kbn_slo_schema.mdx index 57f02527ae298..de52d4d13f3b1 100644 --- a/api_docs/kbn_slo_schema.mdx +++ b/api_docs/kbn_slo_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-slo-schema title: "@kbn/slo-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/slo-schema plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/slo-schema'] --- import kbnSloSchemaObj from './kbn_slo_schema.devdocs.json'; diff --git a/api_docs/kbn_solution_nav_es.mdx b/api_docs/kbn_solution_nav_es.mdx index 166bc52255a21..a6aeaf2d1ce3c 100644 --- a/api_docs/kbn_solution_nav_es.mdx +++ b/api_docs/kbn_solution_nav_es.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-solution-nav-es title: "@kbn/solution-nav-es" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/solution-nav-es plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/solution-nav-es'] --- import kbnSolutionNavEsObj from './kbn_solution_nav_es.devdocs.json'; diff --git a/api_docs/kbn_solution_nav_oblt.mdx b/api_docs/kbn_solution_nav_oblt.mdx index 8146c538764e6..dadd3bdcfcf29 100644 --- a/api_docs/kbn_solution_nav_oblt.mdx +++ b/api_docs/kbn_solution_nav_oblt.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-solution-nav-oblt title: "@kbn/solution-nav-oblt" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/solution-nav-oblt plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/solution-nav-oblt'] --- import kbnSolutionNavObltObj from './kbn_solution_nav_oblt.devdocs.json'; diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index 027ec53e5f5cd..01879167d24fa 100644 --- a/api_docs/kbn_some_dev_log.mdx +++ b/api_docs/kbn_some_dev_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-some-dev-log title: "@kbn/some-dev-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/some-dev-log plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/some-dev-log'] --- import kbnSomeDevLogObj from './kbn_some_dev_log.devdocs.json'; diff --git a/api_docs/kbn_sort_predicates.mdx b/api_docs/kbn_sort_predicates.mdx index fba0c0bc86551..508f27f6014b1 100644 --- a/api_docs/kbn_sort_predicates.mdx +++ b/api_docs/kbn_sort_predicates.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-sort-predicates title: "@kbn/sort-predicates" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/sort-predicates plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sort-predicates'] --- import kbnSortPredicatesObj from './kbn_sort_predicates.devdocs.json'; diff --git a/api_docs/kbn_std.devdocs.json b/api_docs/kbn_std.devdocs.json index b92d1a21b2c29..7dda5e8072d18 100644 --- a/api_docs/kbn_std.devdocs.json +++ b/api_docs/kbn_std.devdocs.json @@ -944,7 +944,7 @@ "\nDetermine if url is outside of this Kibana install." ], "signature": [ - "(url: string, basePath: string) => boolean | undefined" + "(url: string, basePath: string) => boolean" ], "path": "packages/kbn-std/src/is_internal_url.ts", "deprecated": false, diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index 91e2dc1fa14d7..ac15d2dd748cf 100644 --- a/api_docs/kbn_std.mdx +++ b/api_docs/kbn_std.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-std title: "@kbn/std" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/std plugin -date: 2024-05-16 +date: 2024-05-20 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 d2c5020047e94..941fa8455045d 100644 --- a/api_docs/kbn_stdio_dev_helpers.mdx +++ b/api_docs/kbn_stdio_dev_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-stdio-dev-helpers title: "@kbn/stdio-dev-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/stdio-dev-helpers plugin -date: 2024-05-16 +date: 2024-05-20 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 36e8a7f76b79f..fed8552a412dc 100644 --- a/api_docs/kbn_storybook.mdx +++ b/api_docs/kbn_storybook.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-storybook title: "@kbn/storybook" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/storybook plugin -date: 2024-05-16 +date: 2024-05-20 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 42facc713b24c..f5e5d74c37a4f 100644 --- a/api_docs/kbn_telemetry_tools.mdx +++ b/api_docs/kbn_telemetry_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-telemetry-tools title: "@kbn/telemetry-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/telemetry-tools plugin -date: 2024-05-16 +date: 2024-05-20 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 84737a2c9f9b4..3e987fbad81eb 100644 --- a/api_docs/kbn_test.mdx +++ b/api_docs/kbn_test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test title: "@kbn/test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test'] --- import kbnTestObj from './kbn_test.devdocs.json'; diff --git a/api_docs/kbn_test_eui_helpers.mdx b/api_docs/kbn_test_eui_helpers.mdx index b184c22be1544..fde3359592ba7 100644 --- a/api_docs/kbn_test_eui_helpers.mdx +++ b/api_docs/kbn_test_eui_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-eui-helpers title: "@kbn/test-eui-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-eui-helpers plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-eui-helpers'] --- import kbnTestEuiHelpersObj from './kbn_test_eui_helpers.devdocs.json'; diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx index 2236e323df45f..9ef0f4ff4cb60 100644 --- a/api_docs/kbn_test_jest_helpers.mdx +++ b/api_docs/kbn_test_jest_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-jest-helpers title: "@kbn/test-jest-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-jest-helpers plugin -date: 2024-05-16 +date: 2024-05-20 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 a95d2c09c4db2..7b652eb750e23 100644 --- a/api_docs/kbn_test_subj_selector.mdx +++ b/api_docs/kbn_test_subj_selector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-subj-selector title: "@kbn/test-subj-selector" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-subj-selector plugin -date: 2024-05-16 +date: 2024-05-20 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 bfddc274650d6..0db2cf9271d3d 100644 --- a/api_docs/kbn_text_based_editor.mdx +++ b/api_docs/kbn_text_based_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-text-based-editor title: "@kbn/text-based-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/text-based-editor plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/text-based-editor'] --- import kbnTextBasedEditorObj from './kbn_text_based_editor.devdocs.json'; diff --git a/api_docs/kbn_timerange.mdx b/api_docs/kbn_timerange.mdx index 314d215e990ce..cbe053404428e 100644 --- a/api_docs/kbn_timerange.mdx +++ b/api_docs/kbn_timerange.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-timerange title: "@kbn/timerange" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/timerange plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/timerange'] --- import kbnTimerangeObj from './kbn_timerange.devdocs.json'; diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index cfec45e65cc76..1607c7d6d7f03 100644 --- a/api_docs/kbn_tooling_log.mdx +++ b/api_docs/kbn_tooling_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-tooling-log title: "@kbn/tooling-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/tooling-log plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] --- import kbnToolingLogObj from './kbn_tooling_log.devdocs.json'; diff --git a/api_docs/kbn_triggers_actions_ui_types.mdx b/api_docs/kbn_triggers_actions_ui_types.mdx index 1edc8f9da5712..98bf9927a42a3 100644 --- a/api_docs/kbn_triggers_actions_ui_types.mdx +++ b/api_docs/kbn_triggers_actions_ui_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-triggers-actions-ui-types title: "@kbn/triggers-actions-ui-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/triggers-actions-ui-types plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/triggers-actions-ui-types'] --- import kbnTriggersActionsUiTypesObj from './kbn_triggers_actions_ui_types.devdocs.json'; diff --git a/api_docs/kbn_try_in_console.mdx b/api_docs/kbn_try_in_console.mdx index c4d3e26e9546d..6e28c672fca2a 100644 --- a/api_docs/kbn_try_in_console.mdx +++ b/api_docs/kbn_try_in_console.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-try-in-console title: "@kbn/try-in-console" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/try-in-console plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/try-in-console'] --- import kbnTryInConsoleObj from './kbn_try_in_console.devdocs.json'; diff --git a/api_docs/kbn_ts_projects.mdx b/api_docs/kbn_ts_projects.mdx index 176d30b579f26..fd2243c251a09 100644 --- a/api_docs/kbn_ts_projects.mdx +++ b/api_docs/kbn_ts_projects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ts-projects title: "@kbn/ts-projects" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ts-projects plugin -date: 2024-05-16 +date: 2024-05-20 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 dc5a0a8484a76..9cb2ec1eda367 100644 --- a/api_docs/kbn_typed_react_router_config.mdx +++ b/api_docs/kbn_typed_react_router_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-typed-react-router-config title: "@kbn/typed-react-router-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/typed-react-router-config plugin -date: 2024-05-16 +date: 2024-05-20 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 ae43f0539af1a..653baa42b1a7c 100644 --- a/api_docs/kbn_ui_actions_browser.mdx +++ b/api_docs/kbn_ui_actions_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-actions-browser title: "@kbn/ui-actions-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-actions-browser plugin -date: 2024-05-16 +date: 2024-05-20 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 bb0a0f8c06991..b59daa1a3d4ed 100644 --- a/api_docs/kbn_ui_shared_deps_src.mdx +++ b/api_docs/kbn_ui_shared_deps_src.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-shared-deps-src title: "@kbn/ui-shared-deps-src" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-shared-deps-src plugin -date: 2024-05-16 +date: 2024-05-20 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 ef827c0a57ccd..27f5011c2f861 100644 --- a/api_docs/kbn_ui_theme.mdx +++ b/api_docs/kbn_ui_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-theme title: "@kbn/ui-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-theme plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] --- import kbnUiThemeObj from './kbn_ui_theme.devdocs.json'; diff --git a/api_docs/kbn_unified_data_table.mdx b/api_docs/kbn_unified_data_table.mdx index 49959eb9f4656..b3b3f0aa1b648 100644 --- a/api_docs/kbn_unified_data_table.mdx +++ b/api_docs/kbn_unified_data_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-data-table title: "@kbn/unified-data-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-data-table plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-data-table'] --- import kbnUnifiedDataTableObj from './kbn_unified_data_table.devdocs.json'; diff --git a/api_docs/kbn_unified_doc_viewer.mdx b/api_docs/kbn_unified_doc_viewer.mdx index 3791de89a21cf..8072d683064a0 100644 --- a/api_docs/kbn_unified_doc_viewer.mdx +++ b/api_docs/kbn_unified_doc_viewer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-doc-viewer title: "@kbn/unified-doc-viewer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-doc-viewer plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-doc-viewer'] --- import kbnUnifiedDocViewerObj from './kbn_unified_doc_viewer.devdocs.json'; diff --git a/api_docs/kbn_unified_field_list.mdx b/api_docs/kbn_unified_field_list.mdx index 931cda1107e6b..81be1b3177f53 100644 --- a/api_docs/kbn_unified_field_list.mdx +++ b/api_docs/kbn_unified_field_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-field-list title: "@kbn/unified-field-list" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-field-list plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-field-list'] --- import kbnUnifiedFieldListObj from './kbn_unified_field_list.devdocs.json'; diff --git a/api_docs/kbn_unsaved_changes_badge.mdx b/api_docs/kbn_unsaved_changes_badge.mdx index 9352a38315dd7..63f58848e66ff 100644 --- a/api_docs/kbn_unsaved_changes_badge.mdx +++ b/api_docs/kbn_unsaved_changes_badge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unsaved-changes-badge title: "@kbn/unsaved-changes-badge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unsaved-changes-badge plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unsaved-changes-badge'] --- import kbnUnsavedChangesBadgeObj from './kbn_unsaved_changes_badge.devdocs.json'; diff --git a/api_docs/kbn_use_tracked_promise.mdx b/api_docs/kbn_use_tracked_promise.mdx index 32e0ef1be4cb4..ac7a20c6b72e6 100644 --- a/api_docs/kbn_use_tracked_promise.mdx +++ b/api_docs/kbn_use_tracked_promise.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-use-tracked-promise title: "@kbn/use-tracked-promise" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/use-tracked-promise plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/use-tracked-promise'] --- import kbnUseTrackedPromiseObj from './kbn_use_tracked_promise.devdocs.json'; diff --git a/api_docs/kbn_user_profile_components.mdx b/api_docs/kbn_user_profile_components.mdx index 1ee17f2da1447..db5b8249ff3ed 100644 --- a/api_docs/kbn_user_profile_components.mdx +++ b/api_docs/kbn_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-user-profile-components title: "@kbn/user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/user-profile-components plugin -date: 2024-05-16 +date: 2024-05-20 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 724cd472c9551..233ae9ef55e13 100644 --- a/api_docs/kbn_utility_types.mdx +++ b/api_docs/kbn_utility_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types title: "@kbn/utility-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types plugin -date: 2024-05-16 +date: 2024-05-20 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 d41184c744562..a762b3b6da30d 100644 --- a/api_docs/kbn_utility_types_jest.mdx +++ b/api_docs/kbn_utility_types_jest.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types-jest title: "@kbn/utility-types-jest" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types-jest plugin -date: 2024-05-16 +date: 2024-05-20 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 31f07dfbc118d..726214f3a22d9 100644 --- a/api_docs/kbn_utils.mdx +++ b/api_docs/kbn_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utils title: "@kbn/utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utils plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] --- import kbnUtilsObj from './kbn_utils.devdocs.json'; diff --git a/api_docs/kbn_visualization_ui_components.mdx b/api_docs/kbn_visualization_ui_components.mdx index b44bb62993580..ba5a9aead9745 100644 --- a/api_docs/kbn_visualization_ui_components.mdx +++ b/api_docs/kbn_visualization_ui_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-visualization-ui-components title: "@kbn/visualization-ui-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/visualization-ui-components plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/visualization-ui-components'] --- import kbnVisualizationUiComponentsObj from './kbn_visualization_ui_components.devdocs.json'; diff --git a/api_docs/kbn_visualization_utils.mdx b/api_docs/kbn_visualization_utils.mdx index 9d4d5b45e0d5e..9f8cd210a3f24 100644 --- a/api_docs/kbn_visualization_utils.mdx +++ b/api_docs/kbn_visualization_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-visualization-utils title: "@kbn/visualization-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/visualization-utils plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/visualization-utils'] --- import kbnVisualizationUtilsObj from './kbn_visualization_utils.devdocs.json'; diff --git a/api_docs/kbn_xstate_utils.mdx b/api_docs/kbn_xstate_utils.mdx index 321f42394aa4e..a9fb8508b490c 100644 --- a/api_docs/kbn_xstate_utils.mdx +++ b/api_docs/kbn_xstate_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-xstate-utils title: "@kbn/xstate-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/xstate-utils plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/xstate-utils'] --- import kbnXstateUtilsObj from './kbn_xstate_utils.devdocs.json'; diff --git a/api_docs/kbn_yarn_lock_validator.mdx b/api_docs/kbn_yarn_lock_validator.mdx index cf1968ea2b1c5..abc9434abf530 100644 --- a/api_docs/kbn_yarn_lock_validator.mdx +++ b/api_docs/kbn_yarn_lock_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-yarn-lock-validator title: "@kbn/yarn-lock-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/yarn-lock-validator plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/yarn-lock-validator'] --- import kbnYarnLockValidatorObj from './kbn_yarn_lock_validator.devdocs.json'; diff --git a/api_docs/kbn_zod_helpers.mdx b/api_docs/kbn_zod_helpers.mdx index 93dc63ea17678..1fbdf0062a349 100644 --- a/api_docs/kbn_zod_helpers.mdx +++ b/api_docs/kbn_zod_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-zod-helpers title: "@kbn/zod-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/zod-helpers plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/zod-helpers'] --- import kbnZodHelpersObj from './kbn_zod_helpers.devdocs.json'; diff --git a/api_docs/kibana_overview.mdx b/api_docs/kibana_overview.mdx index 357169b6bc610..1717c1602ba58 100644 --- a/api_docs/kibana_overview.mdx +++ b/api_docs/kibana_overview.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaOverview title: "kibanaOverview" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaOverview plugin -date: 2024-05-16 +date: 2024-05-20 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 7a5aef74a8c4a..36a5c3cfd0e65 100644 --- a/api_docs/kibana_react.mdx +++ b/api_docs/kibana_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaReact title: "kibanaReact" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaReact plugin -date: 2024-05-16 +date: 2024-05-20 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 011e618aa23aa..0aa15b1d04d41 100644 --- a/api_docs/kibana_utils.mdx +++ b/api_docs/kibana_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaUtils title: "kibanaUtils" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaUtils plugin -date: 2024-05-16 +date: 2024-05-20 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 919f4a3b6cb8d..7f6ae788d2720 100644 --- a/api_docs/kubernetes_security.mdx +++ b/api_docs/kubernetes_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kubernetesSecurity title: "kubernetesSecurity" image: https://source.unsplash.com/400x175/?github description: API docs for the kubernetesSecurity plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kubernetesSecurity'] --- import kubernetesSecurityObj from './kubernetes_security.devdocs.json'; diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index 75b891aa6e677..c3ef45609b022 100644 --- a/api_docs/lens.mdx +++ b/api_docs/lens.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lens title: "lens" image: https://source.unsplash.com/400x175/?github description: API docs for the lens plugin -date: 2024-05-16 +date: 2024-05-20 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 0e482e6ea7225..df48c59479d8f 100644 --- a/api_docs/license_api_guard.mdx +++ b/api_docs/license_api_guard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseApiGuard title: "licenseApiGuard" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseApiGuard plugin -date: 2024-05-16 +date: 2024-05-20 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 26e85a2bdd468..1ab460056ad87 100644 --- a/api_docs/license_management.mdx +++ b/api_docs/license_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseManagement title: "licenseManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseManagement plugin -date: 2024-05-16 +date: 2024-05-20 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 92d78bfb15e3a..2f482f3c45d9f 100644 --- a/api_docs/licensing.mdx +++ b/api_docs/licensing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licensing title: "licensing" image: https://source.unsplash.com/400x175/?github description: API docs for the licensing plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licensing'] --- import licensingObj from './licensing.devdocs.json'; diff --git a/api_docs/links.mdx b/api_docs/links.mdx index 96d81e43d0780..99e3028a1874b 100644 --- a/api_docs/links.mdx +++ b/api_docs/links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/links title: "links" image: https://source.unsplash.com/400x175/?github description: API docs for the links plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'links'] --- import linksObj from './links.devdocs.json'; diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index abd0d0369f6ef..485ce19c77d8a 100644 --- a/api_docs/lists.mdx +++ b/api_docs/lists.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lists title: "lists" image: https://source.unsplash.com/400x175/?github description: API docs for the lists plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; diff --git a/api_docs/logs_data_access.mdx b/api_docs/logs_data_access.mdx index 00a94ada300b4..9e95c7fceecda 100644 --- a/api_docs/logs_data_access.mdx +++ b/api_docs/logs_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsDataAccess title: "logsDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the logsDataAccess plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logsDataAccess'] --- import logsDataAccessObj from './logs_data_access.devdocs.json'; diff --git a/api_docs/logs_explorer.mdx b/api_docs/logs_explorer.mdx index 7a709b5e0fc4e..f45396785a08f 100644 --- a/api_docs/logs_explorer.mdx +++ b/api_docs/logs_explorer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsExplorer title: "logsExplorer" image: https://source.unsplash.com/400x175/?github description: API docs for the logsExplorer plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logsExplorer'] --- import logsExplorerObj from './logs_explorer.devdocs.json'; diff --git a/api_docs/logs_shared.mdx b/api_docs/logs_shared.mdx index 6ad6f3f106dc5..485723b32754f 100644 --- a/api_docs/logs_shared.mdx +++ b/api_docs/logs_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsShared title: "logsShared" image: https://source.unsplash.com/400x175/?github description: API docs for the logsShared plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logsShared'] --- import logsSharedObj from './logs_shared.devdocs.json'; diff --git a/api_docs/management.mdx b/api_docs/management.mdx index b8b6c19554faa..2111af1994ea7 100644 --- a/api_docs/management.mdx +++ b/api_docs/management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/management title: "management" image: https://source.unsplash.com/400x175/?github description: API docs for the management plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'management'] --- import managementObj from './management.devdocs.json'; diff --git a/api_docs/maps.mdx b/api_docs/maps.mdx index 3f913dd6a1d76..6f79c80cff47a 100644 --- a/api_docs/maps.mdx +++ b/api_docs/maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/maps title: "maps" image: https://source.unsplash.com/400x175/?github description: API docs for the maps plugin -date: 2024-05-16 +date: 2024-05-20 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 e84d3422051a2..07755a84b02c0 100644 --- a/api_docs/maps_ems.mdx +++ b/api_docs/maps_ems.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/mapsEms title: "mapsEms" image: https://source.unsplash.com/400x175/?github description: API docs for the mapsEms plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] --- import mapsEmsObj from './maps_ems.devdocs.json'; diff --git a/api_docs/metrics_data_access.devdocs.json b/api_docs/metrics_data_access.devdocs.json index da97d001411cf..a744b95998f51 100644 --- a/api_docs/metrics_data_access.devdocs.json +++ b/api_docs/metrics_data_access.devdocs.json @@ -1301,7 +1301,107 @@ "InventoryModel", "<", "InventoryMetrics", - ">)[]" + "> | ", + "InventoryModel", + "<", + "InventoryMetricsWithCharts", + "<{ readonly dockerContainerCpuUsage: ", + "LensBaseLayer", + "; readonly dockerContainerMemoryUsage: ", + "LensBaseLayer", + "; readonly k8sContainerCpuUsage: ", + "LensBaseLayer", + "; readonly k8sContainerMemoryUsage: ", + "LensBaseLayer", + "; }, { readonly cpu: { readonly xy: { readonly dockerContainerCpuUsage: { title: string; dataset?: ", + "LensDataset", + " | undefined; chartType: \"xy\"; layers: ({ dataset?: ", + "LensDataset", + " | undefined; yAxis: ", + "LensBaseLayer", + "[]; type: \"series\"; breakdown?: ", + "LensBreakdownConfig", + " | undefined; xAxis: ", + "LensBreakdownConfig", + "; seriesType: \"area\" | \"line\" | \"bar\"; } | { dataset?: ", + "LensDataset", + " | undefined; yAxis: ", + "LensBaseLayer", + "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", + "LensReferenceLineLayer", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"left\" | \"top\" | \"bottom\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + "LensYBoundsConfig", + " | undefined; } & { id: string; }; readonly k8sContainerCpuUsage: { title: string; dataset?: ", + "LensDataset", + " | undefined; chartType: \"xy\"; layers: ({ dataset?: ", + "LensDataset", + " | undefined; yAxis: ", + "LensBaseLayer", + "[]; type: \"series\"; breakdown?: ", + "LensBreakdownConfig", + " | undefined; xAxis: ", + "LensBreakdownConfig", + "; seriesType: \"area\" | \"line\" | \"bar\"; } | { dataset?: ", + "LensDataset", + " | undefined; yAxis: ", + "LensBaseLayer", + "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", + "LensReferenceLineLayer", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"left\" | \"top\" | \"bottom\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + "LensYBoundsConfig", + " | undefined; } & { id: string; }; }; readonly metric: { readonly dockerContainerCpuUsage: { title: string; dataset?: ", + "LensDataset", + " | undefined; label?: string | undefined; filter?: string | undefined; format?: \"string\" | \"number\" | \"duration\" | \"percent\" | \"currency\" | \"bytes\" | \"bits\" | undefined; decimals?: number | undefined; normalizeByUnit?: \"m\" | \"d\" | \"h\" | \"s\" | undefined; compactValues?: boolean | undefined; randomSampling?: number | undefined; useGlobalFilter?: boolean | undefined; seriesColor?: string | undefined; value: string; chartType: \"metric\"; querySecondaryMetric?: string | undefined; queryMaxValue?: string | undefined; breakdown?: ", + "LensBreakdownConfig", + " | undefined; trendLine?: boolean | undefined; subtitle?: string | undefined; } & { id: string; }; readonly k8sContainerCpuUsage: { title: string; dataset?: ", + "LensDataset", + " | undefined; label?: string | undefined; filter?: string | undefined; format?: \"string\" | \"number\" | \"duration\" | \"percent\" | \"currency\" | \"bytes\" | \"bits\" | undefined; decimals?: number | undefined; normalizeByUnit?: \"m\" | \"d\" | \"h\" | \"s\" | undefined; compactValues?: boolean | undefined; randomSampling?: number | undefined; useGlobalFilter?: boolean | undefined; seriesColor?: string | undefined; value: string; chartType: \"metric\"; querySecondaryMetric?: string | undefined; queryMaxValue?: string | undefined; breakdown?: ", + "LensBreakdownConfig", + " | undefined; trendLine?: boolean | undefined; subtitle?: string | undefined; } & { id: string; }; }; }; readonly memory: { xy: { dockerContainerMemoryUsage: { title: string; dataset?: ", + "LensDataset", + " | undefined; chartType: \"xy\"; layers: ({ dataset?: ", + "LensDataset", + " | undefined; yAxis: ", + "LensBaseLayer", + "[]; type: \"series\"; breakdown?: ", + "LensBreakdownConfig", + " | undefined; xAxis: ", + "LensBreakdownConfig", + "; seriesType: \"area\" | \"line\" | \"bar\"; } | { dataset?: ", + "LensDataset", + " | undefined; yAxis: ", + "LensBaseLayer", + "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", + "LensReferenceLineLayer", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"left\" | \"top\" | \"bottom\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + "LensYBoundsConfig", + " | undefined; } & { id: string; }; k8sContainerMemoryUsage: { title: string; dataset?: ", + "LensDataset", + " | undefined; chartType: \"xy\"; layers: ({ dataset?: ", + "LensDataset", + " | undefined; yAxis: ", + "LensBaseLayer", + "[]; type: \"series\"; breakdown?: ", + "LensBreakdownConfig", + " | undefined; xAxis: ", + "LensBreakdownConfig", + "; seriesType: \"area\" | \"line\" | \"bar\"; } | { dataset?: ", + "LensDataset", + " | undefined; yAxis: ", + "LensBaseLayer", + "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", + "LensReferenceLineLayer", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"left\" | \"top\" | \"bottom\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + "LensYBoundsConfig", + " | undefined; } & { id: string; }; }; metric: { dockerContainerMemoryUsage: { title: string; dataset?: ", + "LensDataset", + " | undefined; label?: string | undefined; filter?: string | undefined; format?: \"string\" | \"number\" | \"duration\" | \"percent\" | \"currency\" | \"bytes\" | \"bits\" | undefined; decimals?: number | undefined; normalizeByUnit?: \"m\" | \"d\" | \"h\" | \"s\" | undefined; compactValues?: boolean | undefined; randomSampling?: number | undefined; useGlobalFilter?: boolean | undefined; seriesColor?: string | undefined; value: string; chartType: \"metric\"; querySecondaryMetric?: string | undefined; queryMaxValue?: string | undefined; breakdown?: ", + "LensBreakdownConfig", + " | undefined; trendLine?: boolean | undefined; subtitle?: string | undefined; } & { id: string; }; k8sContainerMemoryUsage: { title: string; dataset?: ", + "LensDataset", + " | undefined; label?: string | undefined; filter?: string | undefined; format?: \"string\" | \"number\" | \"duration\" | \"percent\" | \"currency\" | \"bytes\" | \"bits\" | undefined; decimals?: number | undefined; normalizeByUnit?: \"m\" | \"d\" | \"h\" | \"s\" | undefined; compactValues?: boolean | undefined; randomSampling?: number | undefined; useGlobalFilter?: boolean | undefined; seriesColor?: string | undefined; value: string; chartType: \"metric\"; querySecondaryMetric?: string | undefined; queryMaxValue?: string | undefined; breakdown?: ", + "LensBreakdownConfig", + " | undefined; trendLine?: boolean | undefined; subtitle?: string | undefined; } & { id: string; }; }; }; }>>)[]" ], "path": "x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/index.ts", "deprecated": false, diff --git a/api_docs/metrics_data_access.mdx b/api_docs/metrics_data_access.mdx index ccd39f2162bbf..9a9f33206fdf9 100644 --- a/api_docs/metrics_data_access.mdx +++ b/api_docs/metrics_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/metricsDataAccess title: "metricsDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the metricsDataAccess plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'metricsDataAccess'] --- import metricsDataAccessObj from './metrics_data_access.devdocs.json'; diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index 4f8c6b6fe013a..beaea76c533b7 100644 --- a/api_docs/ml.mdx +++ b/api_docs/ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ml title: "ml" image: https://source.unsplash.com/400x175/?github description: API docs for the ml plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ml'] --- import mlObj from './ml.devdocs.json'; diff --git a/api_docs/mock_idp_plugin.mdx b/api_docs/mock_idp_plugin.mdx index 73a8c8b5c7d31..2069056f50652 100644 --- a/api_docs/mock_idp_plugin.mdx +++ b/api_docs/mock_idp_plugin.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/mockIdpPlugin title: "mockIdpPlugin" image: https://source.unsplash.com/400x175/?github description: API docs for the mockIdpPlugin plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mockIdpPlugin'] --- import mockIdpPluginObj from './mock_idp_plugin.devdocs.json'; diff --git a/api_docs/monitoring.mdx b/api_docs/monitoring.mdx index 40cec41f85dda..61b72c0aa45de 100644 --- a/api_docs/monitoring.mdx +++ b/api_docs/monitoring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoring title: "monitoring" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoring plugin -date: 2024-05-16 +date: 2024-05-20 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 c80f1935b2f6e..b2ca9d714ef6b 100644 --- a/api_docs/monitoring_collection.mdx +++ b/api_docs/monitoring_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoringCollection title: "monitoringCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoringCollection plugin -date: 2024-05-16 +date: 2024-05-20 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 b81fed6e69a37..cd5e7719b1521 100644 --- a/api_docs/navigation.mdx +++ b/api_docs/navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/navigation title: "navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the navigation plugin -date: 2024-05-16 +date: 2024-05-20 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 397df74040477..e7e8a84d0999e 100644 --- a/api_docs/newsfeed.mdx +++ b/api_docs/newsfeed.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/newsfeed title: "newsfeed" image: https://source.unsplash.com/400x175/?github description: API docs for the newsfeed plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/no_data_page.mdx b/api_docs/no_data_page.mdx index 5a3533019838b..2bf4842d3aa9c 100644 --- a/api_docs/no_data_page.mdx +++ b/api_docs/no_data_page.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/noDataPage title: "noDataPage" image: https://source.unsplash.com/400x175/?github description: API docs for the noDataPage plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'noDataPage'] --- import noDataPageObj from './no_data_page.devdocs.json'; diff --git a/api_docs/notifications.mdx b/api_docs/notifications.mdx index 0dd53fb82f210..ab929eaca72a7 100644 --- a/api_docs/notifications.mdx +++ b/api_docs/notifications.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/notifications title: "notifications" image: https://source.unsplash.com/400x175/?github description: API docs for the notifications plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'notifications'] --- import notificationsObj from './notifications.devdocs.json'; diff --git a/api_docs/observability.devdocs.json b/api_docs/observability.devdocs.json index 3cffa85091ec5..977e46df309f5 100644 --- a/api_docs/observability.devdocs.json +++ b/api_docs/observability.devdocs.json @@ -4521,6 +4521,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "observability", + "id": "def-public.enableInfrastructureContainerAssetView", + "type": "string", + "tags": [], + "label": "enableInfrastructureContainerAssetView", + "description": [], + "signature": [ + "\"observability:enableInfrastructureContainerAssetView\"" + ], + "path": "x-pack/plugins/observability_solution/observability/common/ui_settings_keys.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "observability", "id": "def-public.enableInfrastructureHostsView", @@ -10105,6 +10120,90 @@ } ] }, + { + "parentPluginId": "observability", + "id": "def-server.uiSettings.enableInfrastructureContainerAssetView", + "type": "Object", + "tags": [], + "label": "[enableInfrastructureContainerAssetView]", + "description": [], + "path": "x-pack/plugins/observability_solution/observability/server/ui_settings.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "observability", + "id": "def-server.uiSettings.enableInfrastructureContainerAssetView.category", + "type": "Array", + "tags": [], + "label": "category", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/plugins/observability_solution/observability/server/ui_settings.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "observability", + "id": "def-server.uiSettings.enableInfrastructureContainerAssetView.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "x-pack/plugins/observability_solution/observability/server/ui_settings.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "observability", + "id": "def-server.uiSettings.enableInfrastructureContainerAssetView.value", + "type": "boolean", + "tags": [], + "label": "value", + "description": [], + "signature": [ + "false" + ], + "path": "x-pack/plugins/observability_solution/observability/server/ui_settings.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "observability", + "id": "def-server.uiSettings.enableInfrastructureContainerAssetView.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "x-pack/plugins/observability_solution/observability/server/ui_settings.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "observability", + "id": "def-server.uiSettings.enableInfrastructureContainerAssetView.schema", + "type": "Object", + "tags": [], + "label": "schema", + "description": [], + "signature": [ + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.Type", + "text": "Type" + }, + "" + ], + "path": "x-pack/plugins/observability_solution/observability/server/ui_settings.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, { "parentPluginId": "observability", "id": "def-server.uiSettings.enableInfrastructureProfilingIntegration", @@ -14051,6 +14150,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "observability", + "id": "def-common.enableInfrastructureContainerAssetView", + "type": "string", + "tags": [], + "label": "enableInfrastructureContainerAssetView", + "description": [], + "signature": [ + "\"observability:enableInfrastructureContainerAssetView\"" + ], + "path": "x-pack/plugins/observability_solution/observability/common/ui_settings_keys.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "observability", "id": "def-common.enableInfrastructureHostsView", diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index 4262ed9eec0b0..94e78057415cb 100644 --- a/api_docs/observability.mdx +++ b/api_docs/observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observability title: "observability" image: https://source.unsplash.com/400x175/?github description: API docs for the observability plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/ | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 687 | 2 | 678 | 15 | +| 695 | 2 | 686 | 15 | ## Client diff --git a/api_docs/observability_a_i_assistant.mdx b/api_docs/observability_a_i_assistant.mdx index 00c154ef7b9bd..8bbd911ed4503 100644 --- a/api_docs/observability_a_i_assistant.mdx +++ b/api_docs/observability_a_i_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAIAssistant title: "observabilityAIAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAIAssistant plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAIAssistant'] --- import observabilityAIAssistantObj from './observability_a_i_assistant.devdocs.json'; diff --git a/api_docs/observability_a_i_assistant_app.mdx b/api_docs/observability_a_i_assistant_app.mdx index 94079a6a623ab..b11b636b3daa2 100644 --- a/api_docs/observability_a_i_assistant_app.mdx +++ b/api_docs/observability_a_i_assistant_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAIAssistantApp title: "observabilityAIAssistantApp" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAIAssistantApp plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAIAssistantApp'] --- import observabilityAIAssistantAppObj from './observability_a_i_assistant_app.devdocs.json'; diff --git a/api_docs/observability_ai_assistant_management.mdx b/api_docs/observability_ai_assistant_management.mdx index dff0be9912090..d39fa52f9d2c2 100644 --- a/api_docs/observability_ai_assistant_management.mdx +++ b/api_docs/observability_ai_assistant_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAiAssistantManagement title: "observabilityAiAssistantManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAiAssistantManagement plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAiAssistantManagement'] --- import observabilityAiAssistantManagementObj from './observability_ai_assistant_management.devdocs.json'; diff --git a/api_docs/observability_logs_explorer.mdx b/api_docs/observability_logs_explorer.mdx index de588320fe4d1..e7363949fffe8 100644 --- a/api_docs/observability_logs_explorer.mdx +++ b/api_docs/observability_logs_explorer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityLogsExplorer title: "observabilityLogsExplorer" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityLogsExplorer plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityLogsExplorer'] --- import observabilityLogsExplorerObj from './observability_logs_explorer.devdocs.json'; diff --git a/api_docs/observability_onboarding.mdx b/api_docs/observability_onboarding.mdx index b6c68453a523a..2f487c6187867 100644 --- a/api_docs/observability_onboarding.mdx +++ b/api_docs/observability_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityOnboarding title: "observabilityOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityOnboarding plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityOnboarding'] --- import observabilityOnboardingObj from './observability_onboarding.devdocs.json'; diff --git a/api_docs/observability_shared.devdocs.json b/api_docs/observability_shared.devdocs.json index 351f5ce7c2a1d..e44212f483bad 100644 --- a/api_docs/observability_shared.devdocs.json +++ b/api_docs/observability_shared.devdocs.json @@ -3488,6 +3488,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "observabilityShared", + "id": "def-public.INVENTORY_LOCATOR_ID", + "type": "string", + "tags": [], + "label": "INVENTORY_LOCATOR_ID", + "description": [], + "signature": [ + "\"INVENTORY_LOCATOR\"" + ], + "path": "x-pack/plugins/observability_solution/observability_shared/public/locators/infra/inventory_locator.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "observabilityShared", "id": "def-public.LazyObservabilityPageTemplateProps", diff --git a/api_docs/observability_shared.mdx b/api_docs/observability_shared.mdx index a9e2a3bac29c6..d5a79adc57e03 100644 --- a/api_docs/observability_shared.mdx +++ b/api_docs/observability_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityShared title: "observabilityShared" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityShared plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityShared'] --- import observabilitySharedObj from './observability_shared.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/observability-ui](https://github.com/orgs/elastic/teams/observ | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 354 | 1 | 349 | 22 | +| 355 | 1 | 350 | 22 | ## Client diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index 8a9550166fc8d..caeef587b92dd 100644 --- a/api_docs/osquery.mdx +++ b/api_docs/osquery.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/osquery title: "osquery" image: https://source.unsplash.com/400x175/?github description: API docs for the osquery plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery'] --- import osqueryObj from './osquery.devdocs.json'; diff --git a/api_docs/painless_lab.mdx b/api_docs/painless_lab.mdx index 2bb9abcc168ee..f3994662ff5df 100644 --- a/api_docs/painless_lab.mdx +++ b/api_docs/painless_lab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/painlessLab title: "painlessLab" image: https://source.unsplash.com/400x175/?github description: API docs for the painlessLab plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'painlessLab'] --- import painlessLabObj from './painless_lab.devdocs.json'; diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index 3e64d151f37b2..5a6ccd7f4b458 100644 --- a/api_docs/plugin_directory.mdx +++ b/api_docs/plugin_directory.mdx @@ -7,7 +7,7 @@ id: kibDevDocsPluginDirectory slug: /kibana-dev-docs/api-meta/plugin-api-directory title: Directory description: Directory of public APIs available through plugins or packages. -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -15,19 +15,19 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Count | Plugins or Packages with a
public API | Number of teams | |--------------|----------|------------------------| -| 796 | 684 | 43 | +| 797 | 685 | 43 | ### Public API health stats | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 48169 | 241 | 36749 | 1858 | +| 48219 | 241 | 36798 | 1860 | ## 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) | - | 301 | 0 | 295 | 32 | +| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 303 | 0 | 297 | 32 | | | [@elastic/appex-sharedux @elastic/kibana-management](https://github.com/orgs/elastic/teams/appex-sharedux ) | - | 2 | 0 | 2 | 0 | | | [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs-knowledge-team) | - | 4 | 0 | 4 | 1 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | AIOps plugin maintained by ML team. | 67 | 0 | 4 | 1 | @@ -142,13 +142,13 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 17 | 0 | 17 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 3 | 0 | 3 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 2 | 0 | 2 | 1 | -| | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | - | 687 | 2 | 678 | 15 | +| | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | - | 695 | 2 | 686 | 15 | | | [@elastic/obs-ai-assistant](https://github.com/orgs/elastic/teams/obs-ai-assistant) | - | 253 | 1 | 251 | 26 | | | [@elastic/obs-ai-assistant](https://github.com/orgs/elastic/teams/obs-ai-assistant) | - | 2 | 0 | 2 | 0 | | | [@elastic/obs-ai-assistant](https://github.com/orgs/elastic/teams/obs-ai-assistant) | - | 2 | 0 | 2 | 0 | | | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | This plugin exposes and registers observability log consumption features. | 21 | 0 | 21 | 1 | | | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | - | 16 | 0 | 16 | 0 | -| | [@elastic/observability-ui](https://github.com/orgs/elastic/teams/observability-ui) | - | 354 | 1 | 349 | 22 | +| | [@elastic/observability-ui](https://github.com/orgs/elastic/teams/observability-ui) | - | 355 | 1 | 350 | 22 | | | [@elastic/security-defend-workflows](https://github.com/orgs/elastic/teams/security-defend-workflows) | - | 23 | 0 | 23 | 7 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 2 | 0 | 2 | 0 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds a standardized Presentation panel which allows any forward ref component to interface with various Kibana systems. | 11 | 0 | 11 | 4 | @@ -249,7 +249,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 18 | 0 | 18 | 0 | | | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 4 | 0 | 4 | 0 | | | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 49 | 0 | 49 | 8 | -| | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 191 | 0 | 191 | 28 | +| | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 193 | 0 | 193 | 30 | | | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 11 | 0 | 11 | 0 | | | [@elastic/kibana-qa](https://github.com/orgs/elastic/teams/kibana-qa) | - | 12 | 0 | 12 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 4 | 0 | 1 | 0 | @@ -275,7 +275,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 3 | 0 | 3 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 8 | 0 | 8 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 47 | 0 | 31 | 3 | -| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 192 | 1 | 126 | 0 | +| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 194 | 1 | 127 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 0 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 7 | 0 | 7 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 4 | 0 | @@ -395,7 +395,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 112 | 1 | 0 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 356 | 1 | 5 | 2 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 11 | 0 | 11 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 225 | 0 | 182 | 11 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 226 | 0 | 183 | 11 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 5 | 0 | 5 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 2 | 0 | 1 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 6 | 0 | 6 | 0 | @@ -482,8 +482,9 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 33 | 0 | 24 | 1 | | | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | - | 13 | 0 | 5 | 0 | | | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | - | 35 | 0 | 34 | 0 | -| | [@elastic/security-generative-ai](https://github.com/orgs/elastic/teams/security-generative-ai) | - | 173 | 0 | 146 | 9 | +| | [@elastic/security-generative-ai](https://github.com/orgs/elastic/teams/security-generative-ai) | - | 174 | 0 | 147 | 9 | | | [@elastic/security-generative-ai](https://github.com/orgs/elastic/teams/security-generative-ai) | - | 232 | 0 | 217 | 2 | +| | [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs-knowledge-team) | - | 19 | 0 | 19 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 52 | 0 | 37 | 7 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 32 | 0 | 19 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 7 | 0 | 3 | 0 | @@ -538,7 +539,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 23 | 0 | 7 | 0 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 8 | 0 | 2 | 3 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 45 | 0 | 0 | 0 | -| | [@elastic/appex-sharedux @elastic/kibana-management](https://github.com/orgs/elastic/teams/appex-sharedux ) | - | 138 | 0 | 135 | 0 | +| | [@elastic/appex-sharedux @elastic/kibana-management](https://github.com/orgs/elastic/teams/appex-sharedux ) | - | 139 | 0 | 136 | 0 | | | [@elastic/appex-sharedux @elastic/kibana-management](https://github.com/orgs/elastic/teams/appex-sharedux ) | - | 20 | 0 | 11 | 0 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 88 | 0 | 10 | 0 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 56 | 0 | 6 | 0 | @@ -621,8 +622,8 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@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) | - | 125 | 0 | 122 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 2 | 0 | 2 | 0 | -| | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | - | 74 | 0 | 74 | 0 | -| | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | - | 3661 | 0 | 3661 | 0 | +| | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | - | 76 | 0 | 76 | 0 | +| | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | - | 3672 | 0 | 3672 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 18 | 1 | 17 | 1 | | | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | - | 25 | 0 | 25 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 20 | 0 | 18 | 1 | diff --git a/api_docs/presentation_panel.mdx b/api_docs/presentation_panel.mdx index 2145e99247362..8c268e1f95f1b 100644 --- a/api_docs/presentation_panel.mdx +++ b/api_docs/presentation_panel.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationPanel title: "presentationPanel" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationPanel plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationPanel'] --- import presentationPanelObj from './presentation_panel.devdocs.json'; diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index da95f22c5d43b..c3fbeb48c1cb4 100644 --- a/api_docs/presentation_util.mdx +++ b/api_docs/presentation_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationUtil title: "presentationUtil" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationUtil plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationUtil'] --- import presentationUtilObj from './presentation_util.devdocs.json'; diff --git a/api_docs/profiling.devdocs.json b/api_docs/profiling.devdocs.json index c68d7dcd3af81..01192dc0b13f4 100644 --- a/api_docs/profiling.devdocs.json +++ b/api_docs/profiling.devdocs.json @@ -53,7 +53,7 @@ "label": "ProfilingConfig", "description": [], "signature": [ - "{ readonly elasticsearch?: Readonly<{} & { username: string; hosts: string; password: string; }> | undefined; readonly symbolizer?: Readonly<{ telemetry?: boolean | undefined; host?: string | undefined; tls_enabled?: boolean | undefined; tls_supported_protocols?: string[] | undefined; tls_certificate_path?: string | undefined; tls_key_path?: string | undefined; } & {}> | undefined; readonly collector?: Readonly<{ telemetry?: boolean | undefined; host?: string | undefined; tls_enabled?: boolean | undefined; tls_supported_protocols?: string[] | undefined; tls_certificate_path?: string | undefined; tls_key_path?: string | undefined; } & {}> | undefined; readonly enabled: boolean; }" + "{ readonly elasticsearch?: Readonly<{} & { username: string; password: string; hosts: string; }> | undefined; readonly symbolizer?: Readonly<{ telemetry?: boolean | undefined; host?: string | undefined; tls_enabled?: boolean | undefined; tls_supported_protocols?: string[] | undefined; tls_certificate_path?: string | undefined; tls_key_path?: string | undefined; } & {}> | undefined; readonly collector?: Readonly<{ telemetry?: boolean | undefined; host?: string | undefined; tls_enabled?: boolean | undefined; tls_supported_protocols?: string[] | undefined; tls_certificate_path?: string | undefined; tls_key_path?: string | undefined; } & {}> | undefined; readonly enabled: boolean; }" ], "path": "x-pack/plugins/observability_solution/profiling/server/index.ts", "deprecated": false, diff --git a/api_docs/profiling.mdx b/api_docs/profiling.mdx index 59bcde1e25540..24562df3c52b6 100644 --- a/api_docs/profiling.mdx +++ b/api_docs/profiling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profiling title: "profiling" image: https://source.unsplash.com/400x175/?github description: API docs for the profiling plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profiling'] --- import profilingObj from './profiling.devdocs.json'; diff --git a/api_docs/profiling_data_access.devdocs.json b/api_docs/profiling_data_access.devdocs.json index 12988cabdf640..5e9cd79d51299 100644 --- a/api_docs/profiling_data_access.devdocs.json +++ b/api_docs/profiling_data_access.devdocs.json @@ -22,7 +22,7 @@ "label": "ProfilingConfig", "description": [], "signature": [ - "{ readonly elasticsearch?: Readonly<{} & { username: string; hosts: string; password: string; }> | undefined; }" + "{ readonly elasticsearch?: Readonly<{} & { username: string; password: string; hosts: string; }> | undefined; }" ], "path": "x-pack/plugins/observability_solution/profiling_data_access/server/index.ts", "deprecated": false, diff --git a/api_docs/profiling_data_access.mdx b/api_docs/profiling_data_access.mdx index 7e50e73193555..815446290093b 100644 --- a/api_docs/profiling_data_access.mdx +++ b/api_docs/profiling_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profilingDataAccess title: "profilingDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the profilingDataAccess plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profilingDataAccess'] --- import profilingDataAccessObj from './profiling_data_access.devdocs.json'; diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index 3c2a53eb016ad..8a9b064ae4b3e 100644 --- a/api_docs/remote_clusters.mdx +++ b/api_docs/remote_clusters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/remoteClusters title: "remoteClusters" image: https://source.unsplash.com/400x175/?github description: API docs for the remoteClusters plugin -date: 2024-05-16 +date: 2024-05-20 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 2a05fe65cb673..6640e4af8b91d 100644 --- a/api_docs/reporting.mdx +++ b/api_docs/reporting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/reporting title: "reporting" image: https://source.unsplash.com/400x175/?github description: API docs for the reporting plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'reporting'] --- import reportingObj from './reporting.devdocs.json'; diff --git a/api_docs/rollup.mdx b/api_docs/rollup.mdx index 23bf7e53d0667..7a0c154687213 100644 --- a/api_docs/rollup.mdx +++ b/api_docs/rollup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/rollup title: "rollup" image: https://source.unsplash.com/400x175/?github description: API docs for the rollup plugin -date: 2024-05-16 +date: 2024-05-20 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 e36801de65fe5..08ce12e279590 100644 --- a/api_docs/rule_registry.mdx +++ b/api_docs/rule_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ruleRegistry title: "ruleRegistry" image: https://source.unsplash.com/400x175/?github description: API docs for the ruleRegistry plugin -date: 2024-05-16 +date: 2024-05-20 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 aacbce38894b8..fa58345d738ca 100644 --- a/api_docs/runtime_fields.mdx +++ b/api_docs/runtime_fields.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/runtimeFields title: "runtimeFields" image: https://source.unsplash.com/400x175/?github description: API docs for the runtimeFields plugin -date: 2024-05-16 +date: 2024-05-20 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 2bc1a39cc90c6..2bec6748e53cd 100644 --- a/api_docs/saved_objects.mdx +++ b/api_docs/saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjects title: "savedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjects plugin -date: 2024-05-16 +date: 2024-05-20 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 46ae5fbff6659..5daf9853f828f 100644 --- a/api_docs/saved_objects_finder.mdx +++ b/api_docs/saved_objects_finder.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsFinder title: "savedObjectsFinder" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsFinder plugin -date: 2024-05-16 +date: 2024-05-20 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 e20f831efa6aa..e228ca4121902 100644 --- a/api_docs/saved_objects_management.mdx +++ b/api_docs/saved_objects_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsManagement title: "savedObjectsManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsManagement plugin -date: 2024-05-16 +date: 2024-05-20 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 f28487633f2da..e97860955ca84 100644 --- a/api_docs/saved_objects_tagging.mdx +++ b/api_docs/saved_objects_tagging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTagging title: "savedObjectsTagging" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTagging plugin -date: 2024-05-16 +date: 2024-05-20 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 3bc20357ed071..0d574769a21cd 100644 --- a/api_docs/saved_objects_tagging_oss.mdx +++ b/api_docs/saved_objects_tagging_oss.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTaggingOss title: "savedObjectsTaggingOss" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTaggingOss plugin -date: 2024-05-16 +date: 2024-05-20 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 b8692decda8e9..16bc7de0b5688 100644 --- a/api_docs/saved_search.mdx +++ b/api_docs/saved_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedSearch title: "savedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the savedSearch plugin -date: 2024-05-16 +date: 2024-05-20 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 72f430068b701..2e07d5a49f159 100644 --- a/api_docs/screenshot_mode.mdx +++ b/api_docs/screenshot_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotMode title: "screenshotMode" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotMode plugin -date: 2024-05-16 +date: 2024-05-20 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 712304b5e4224..ae5c74157c1ba 100644 --- a/api_docs/screenshotting.mdx +++ b/api_docs/screenshotting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotting title: "screenshotting" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotting plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; diff --git a/api_docs/search_connectors.mdx b/api_docs/search_connectors.mdx index 1ac19f6cbeeb9..9a37ce5bc337b 100644 --- a/api_docs/search_connectors.mdx +++ b/api_docs/search_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchConnectors title: "searchConnectors" image: https://source.unsplash.com/400x175/?github description: API docs for the searchConnectors plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchConnectors'] --- import searchConnectorsObj from './search_connectors.devdocs.json'; diff --git a/api_docs/search_notebooks.mdx b/api_docs/search_notebooks.mdx index e10f9cbb278ac..4613c7395bbdc 100644 --- a/api_docs/search_notebooks.mdx +++ b/api_docs/search_notebooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchNotebooks title: "searchNotebooks" image: https://source.unsplash.com/400x175/?github description: API docs for the searchNotebooks plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchNotebooks'] --- import searchNotebooksObj from './search_notebooks.devdocs.json'; diff --git a/api_docs/search_playground.mdx b/api_docs/search_playground.mdx index ad30d44332bc3..018d2605b2f76 100644 --- a/api_docs/search_playground.mdx +++ b/api_docs/search_playground.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchPlayground title: "searchPlayground" image: https://source.unsplash.com/400x175/?github description: API docs for the searchPlayground plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchPlayground'] --- import searchPlaygroundObj from './search_playground.devdocs.json'; diff --git a/api_docs/security.mdx b/api_docs/security.mdx index 9cc1a66ad2cad..493597afeb4da 100644 --- a/api_docs/security.mdx +++ b/api_docs/security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/security title: "security" image: https://source.unsplash.com/400x175/?github description: API docs for the security plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'security'] --- import securityObj from './security.devdocs.json'; diff --git a/api_docs/security_solution.devdocs.json b/api_docs/security_solution.devdocs.json index a5ac269fd642e..4f10cc0529917 100644 --- a/api_docs/security_solution.devdocs.json +++ b/api_docs/security_solution.devdocs.json @@ -485,7 +485,7 @@ "\nExperimental flag needed to enable the link" ], "signature": [ - "\"assistantModelEvaluation\" | \"tGridEnabled\" | \"tGridEventRenderedViewEnabled\" | \"excludePoliciesInFilterEnabled\" | \"kubernetesEnabled\" | \"donutChartEmbeddablesEnabled\" | \"previewTelemetryUrlEnabled\" | \"insightsRelatedAlertsByProcessAncestry\" | \"extendedRuleExecutionLoggingEnabled\" | \"socTrendsEnabled\" | \"responseActionsEnabled\" | \"endpointResponseActionsEnabled\" | \"responseActionUploadEnabled\" | \"automatedProcessActionsEnabled\" | \"responseActionsSentinelOneV1Enabled\" | \"responseActionsSentinelOneV2Enabled\" | \"responseActionsSentinelOneGetFileEnabled\" | \"agentStatusClientEnabled\" | \"responseActionsCrowdstrikeManualHostIsolationEnabled\" | \"alertsPageChartsEnabled\" | \"alertTypeEnabled\" | \"expandableFlyoutInCreateRuleEnabled\" | \"expandableEventFlyoutEnabled\" | \"expandableTimelineFlyoutEnabled\" | \"alertsPageFiltersEnabled\" | \"newUserDetailsFlyout\" | \"newUserDetailsFlyoutManagedUser\" | \"newHostDetailsFlyout\" | \"riskScoringPersistence\" | \"riskScoringRoutesEnabled\" | \"esqlRulesDisabled\" | \"protectionUpdatesEnabled\" | \"disableTimelineSaveTour\" | \"riskEnginePrivilegesRouteEnabled\" | \"sentinelOneDataInAnalyzerEnabled\" | \"sentinelOneManualHostActionsEnabled\" | \"crowdstrikeDataInAnalyzerEnabled\" | \"jamfDataInAnalyzerEnabled\" | \"jsonPrebuiltRulesDiffingEnabled\" | \"timelineEsqlTabDisabled\" | \"unifiedComponentsInTimelineEnabled\" | \"analyzerDatePickersAndSourcererDisabled\" | \"perFieldPrebuiltRulesDiffingEnabled\" | \"malwareOnWriteScanOptionAvailable\" | \"aiAssistantFlyoutMode\" | \"valueListItemsModalEnabled\" | \"bulkCustomHighlightedFieldsEnabled\" | undefined" + "\"assistantModelEvaluation\" | \"tGridEnabled\" | \"tGridEventRenderedViewEnabled\" | \"excludePoliciesInFilterEnabled\" | \"kubernetesEnabled\" | \"donutChartEmbeddablesEnabled\" | \"previewTelemetryUrlEnabled\" | \"insightsRelatedAlertsByProcessAncestry\" | \"extendedRuleExecutionLoggingEnabled\" | \"socTrendsEnabled\" | \"responseActionsEnabled\" | \"endpointResponseActionsEnabled\" | \"responseActionUploadEnabled\" | \"automatedProcessActionsEnabled\" | \"responseActionsSentinelOneV1Enabled\" | \"responseActionsSentinelOneV2Enabled\" | \"responseActionsSentinelOneGetFileEnabled\" | \"agentStatusClientEnabled\" | \"responseActionsCrowdstrikeManualHostIsolationEnabled\" | \"alertsPageChartsEnabled\" | \"alertTypeEnabled\" | \"expandableFlyoutInCreateRuleEnabled\" | \"expandableEventFlyoutEnabled\" | \"expandableTimelineFlyoutEnabled\" | \"alertsPageFiltersEnabled\" | \"newUserDetailsFlyout\" | \"newUserDetailsFlyoutManagedUser\" | \"newHostDetailsFlyout\" | \"riskScoringPersistence\" | \"riskScoringRoutesEnabled\" | \"esqlRulesDisabled\" | \"protectionUpdatesEnabled\" | \"disableTimelineSaveTour\" | \"riskEnginePrivilegesRouteEnabled\" | \"sentinelOneDataInAnalyzerEnabled\" | \"sentinelOneManualHostActionsEnabled\" | \"crowdstrikeDataInAnalyzerEnabled\" | \"jamfDataInAnalyzerEnabled\" | \"jsonPrebuiltRulesDiffingEnabled\" | \"timelineEsqlTabDisabled\" | \"unifiedComponentsInTimelineEnabled\" | \"analyzerDatePickersAndSourcererDisabled\" | \"perFieldPrebuiltRulesDiffingEnabled\" | \"malwareOnWriteScanOptionAvailable\" | \"unifiedManifestEnabled\" | \"aiAssistantFlyoutMode\" | \"valueListItemsModalEnabled\" | \"bulkCustomHighlightedFieldsEnabled\" | undefined" ], "path": "x-pack/plugins/security_solution/public/common/links/types.ts", "deprecated": false, @@ -565,7 +565,7 @@ "\nExperimental flag needed to disable the link. Opposite of experimentalKey" ], "signature": [ - "\"assistantModelEvaluation\" | \"tGridEnabled\" | \"tGridEventRenderedViewEnabled\" | \"excludePoliciesInFilterEnabled\" | \"kubernetesEnabled\" | \"donutChartEmbeddablesEnabled\" | \"previewTelemetryUrlEnabled\" | \"insightsRelatedAlertsByProcessAncestry\" | \"extendedRuleExecutionLoggingEnabled\" | \"socTrendsEnabled\" | \"responseActionsEnabled\" | \"endpointResponseActionsEnabled\" | \"responseActionUploadEnabled\" | \"automatedProcessActionsEnabled\" | \"responseActionsSentinelOneV1Enabled\" | \"responseActionsSentinelOneV2Enabled\" | \"responseActionsSentinelOneGetFileEnabled\" | \"agentStatusClientEnabled\" | \"responseActionsCrowdstrikeManualHostIsolationEnabled\" | \"alertsPageChartsEnabled\" | \"alertTypeEnabled\" | \"expandableFlyoutInCreateRuleEnabled\" | \"expandableEventFlyoutEnabled\" | \"expandableTimelineFlyoutEnabled\" | \"alertsPageFiltersEnabled\" | \"newUserDetailsFlyout\" | \"newUserDetailsFlyoutManagedUser\" | \"newHostDetailsFlyout\" | \"riskScoringPersistence\" | \"riskScoringRoutesEnabled\" | \"esqlRulesDisabled\" | \"protectionUpdatesEnabled\" | \"disableTimelineSaveTour\" | \"riskEnginePrivilegesRouteEnabled\" | \"sentinelOneDataInAnalyzerEnabled\" | \"sentinelOneManualHostActionsEnabled\" | \"crowdstrikeDataInAnalyzerEnabled\" | \"jamfDataInAnalyzerEnabled\" | \"jsonPrebuiltRulesDiffingEnabled\" | \"timelineEsqlTabDisabled\" | \"unifiedComponentsInTimelineEnabled\" | \"analyzerDatePickersAndSourcererDisabled\" | \"perFieldPrebuiltRulesDiffingEnabled\" | \"malwareOnWriteScanOptionAvailable\" | \"aiAssistantFlyoutMode\" | \"valueListItemsModalEnabled\" | \"bulkCustomHighlightedFieldsEnabled\" | undefined" + "\"assistantModelEvaluation\" | \"tGridEnabled\" | \"tGridEventRenderedViewEnabled\" | \"excludePoliciesInFilterEnabled\" | \"kubernetesEnabled\" | \"donutChartEmbeddablesEnabled\" | \"previewTelemetryUrlEnabled\" | \"insightsRelatedAlertsByProcessAncestry\" | \"extendedRuleExecutionLoggingEnabled\" | \"socTrendsEnabled\" | \"responseActionsEnabled\" | \"endpointResponseActionsEnabled\" | \"responseActionUploadEnabled\" | \"automatedProcessActionsEnabled\" | \"responseActionsSentinelOneV1Enabled\" | \"responseActionsSentinelOneV2Enabled\" | \"responseActionsSentinelOneGetFileEnabled\" | \"agentStatusClientEnabled\" | \"responseActionsCrowdstrikeManualHostIsolationEnabled\" | \"alertsPageChartsEnabled\" | \"alertTypeEnabled\" | \"expandableFlyoutInCreateRuleEnabled\" | \"expandableEventFlyoutEnabled\" | \"expandableTimelineFlyoutEnabled\" | \"alertsPageFiltersEnabled\" | \"newUserDetailsFlyout\" | \"newUserDetailsFlyoutManagedUser\" | \"newHostDetailsFlyout\" | \"riskScoringPersistence\" | \"riskScoringRoutesEnabled\" | \"esqlRulesDisabled\" | \"protectionUpdatesEnabled\" | \"disableTimelineSaveTour\" | \"riskEnginePrivilegesRouteEnabled\" | \"sentinelOneDataInAnalyzerEnabled\" | \"sentinelOneManualHostActionsEnabled\" | \"crowdstrikeDataInAnalyzerEnabled\" | \"jamfDataInAnalyzerEnabled\" | \"jsonPrebuiltRulesDiffingEnabled\" | \"timelineEsqlTabDisabled\" | \"unifiedComponentsInTimelineEnabled\" | \"analyzerDatePickersAndSourcererDisabled\" | \"perFieldPrebuiltRulesDiffingEnabled\" | \"malwareOnWriteScanOptionAvailable\" | \"unifiedManifestEnabled\" | \"aiAssistantFlyoutMode\" | \"valueListItemsModalEnabled\" | \"bulkCustomHighlightedFieldsEnabled\" | undefined" ], "path": "x-pack/plugins/security_solution/public/common/links/types.ts", "deprecated": false, @@ -1964,7 +1964,7 @@ "label": "experimentalFeatures", "description": [], "signature": [ - "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly automatedProcessActionsEnabled: boolean; readonly responseActionsSentinelOneV1Enabled: boolean; readonly responseActionsSentinelOneV2Enabled: boolean; readonly responseActionsSentinelOneGetFileEnabled: boolean; readonly agentStatusClientEnabled: boolean; readonly responseActionsCrowdstrikeManualHostIsolationEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutInCreateRuleEnabled: boolean; readonly expandableEventFlyoutEnabled: boolean; readonly expandableTimelineFlyoutEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly newUserDetailsFlyout: boolean; readonly newUserDetailsFlyoutManagedUser: boolean; readonly newHostDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly sentinelOneDataInAnalyzerEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; readonly crowdstrikeDataInAnalyzerEnabled: boolean; readonly jamfDataInAnalyzerEnabled: boolean; readonly jsonPrebuiltRulesDiffingEnabled: boolean; readonly timelineEsqlTabDisabled: boolean; readonly unifiedComponentsInTimelineEnabled: boolean; readonly analyzerDatePickersAndSourcererDisabled: boolean; readonly perFieldPrebuiltRulesDiffingEnabled: boolean; readonly malwareOnWriteScanOptionAvailable: boolean; readonly aiAssistantFlyoutMode: boolean; readonly valueListItemsModalEnabled: boolean; readonly bulkCustomHighlightedFieldsEnabled: boolean; }" + "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly automatedProcessActionsEnabled: boolean; readonly responseActionsSentinelOneV1Enabled: boolean; readonly responseActionsSentinelOneV2Enabled: boolean; readonly responseActionsSentinelOneGetFileEnabled: boolean; readonly agentStatusClientEnabled: boolean; readonly responseActionsCrowdstrikeManualHostIsolationEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutInCreateRuleEnabled: boolean; readonly expandableEventFlyoutEnabled: boolean; readonly expandableTimelineFlyoutEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly newUserDetailsFlyout: boolean; readonly newUserDetailsFlyoutManagedUser: boolean; readonly newHostDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly sentinelOneDataInAnalyzerEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; readonly crowdstrikeDataInAnalyzerEnabled: boolean; readonly jamfDataInAnalyzerEnabled: boolean; readonly jsonPrebuiltRulesDiffingEnabled: boolean; readonly timelineEsqlTabDisabled: boolean; readonly unifiedComponentsInTimelineEnabled: boolean; readonly analyzerDatePickersAndSourcererDisabled: boolean; readonly perFieldPrebuiltRulesDiffingEnabled: boolean; readonly malwareOnWriteScanOptionAvailable: boolean; readonly unifiedManifestEnabled: boolean; readonly aiAssistantFlyoutMode: boolean; readonly valueListItemsModalEnabled: boolean; readonly bulkCustomHighlightedFieldsEnabled: boolean; }" ], "path": "x-pack/plugins/security_solution/public/types.ts", "deprecated": false, @@ -3054,7 +3054,7 @@ "\nThe security solution generic experimental features" ], "signature": [ - "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly automatedProcessActionsEnabled: boolean; readonly responseActionsSentinelOneV1Enabled: boolean; readonly responseActionsSentinelOneV2Enabled: boolean; readonly responseActionsSentinelOneGetFileEnabled: boolean; readonly agentStatusClientEnabled: boolean; readonly responseActionsCrowdstrikeManualHostIsolationEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutInCreateRuleEnabled: boolean; readonly expandableEventFlyoutEnabled: boolean; readonly expandableTimelineFlyoutEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly newUserDetailsFlyout: boolean; readonly newUserDetailsFlyoutManagedUser: boolean; readonly newHostDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly sentinelOneDataInAnalyzerEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; readonly crowdstrikeDataInAnalyzerEnabled: boolean; readonly jamfDataInAnalyzerEnabled: boolean; readonly jsonPrebuiltRulesDiffingEnabled: boolean; readonly timelineEsqlTabDisabled: boolean; readonly unifiedComponentsInTimelineEnabled: boolean; readonly analyzerDatePickersAndSourcererDisabled: boolean; readonly perFieldPrebuiltRulesDiffingEnabled: boolean; readonly malwareOnWriteScanOptionAvailable: boolean; readonly aiAssistantFlyoutMode: boolean; readonly valueListItemsModalEnabled: boolean; readonly bulkCustomHighlightedFieldsEnabled: boolean; }" + "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly automatedProcessActionsEnabled: boolean; readonly responseActionsSentinelOneV1Enabled: boolean; readonly responseActionsSentinelOneV2Enabled: boolean; readonly responseActionsSentinelOneGetFileEnabled: boolean; readonly agentStatusClientEnabled: boolean; readonly responseActionsCrowdstrikeManualHostIsolationEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutInCreateRuleEnabled: boolean; readonly expandableEventFlyoutEnabled: boolean; readonly expandableTimelineFlyoutEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly newUserDetailsFlyout: boolean; readonly newUserDetailsFlyoutManagedUser: boolean; readonly newHostDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly sentinelOneDataInAnalyzerEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; readonly crowdstrikeDataInAnalyzerEnabled: boolean; readonly jamfDataInAnalyzerEnabled: boolean; readonly jsonPrebuiltRulesDiffingEnabled: boolean; readonly timelineEsqlTabDisabled: boolean; readonly unifiedComponentsInTimelineEnabled: boolean; readonly analyzerDatePickersAndSourcererDisabled: boolean; readonly perFieldPrebuiltRulesDiffingEnabled: boolean; readonly malwareOnWriteScanOptionAvailable: boolean; readonly unifiedManifestEnabled: boolean; readonly aiAssistantFlyoutMode: boolean; readonly valueListItemsModalEnabled: boolean; readonly bulkCustomHighlightedFieldsEnabled: boolean; }" ], "path": "x-pack/plugins/security_solution/server/plugin_contract.ts", "deprecated": false, @@ -3230,7 +3230,7 @@ "label": "ExperimentalFeatures", "description": [], "signature": [ - "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly automatedProcessActionsEnabled: boolean; readonly responseActionsSentinelOneV1Enabled: boolean; readonly responseActionsSentinelOneV2Enabled: boolean; readonly responseActionsSentinelOneGetFileEnabled: boolean; readonly agentStatusClientEnabled: boolean; readonly responseActionsCrowdstrikeManualHostIsolationEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutInCreateRuleEnabled: boolean; readonly expandableEventFlyoutEnabled: boolean; readonly expandableTimelineFlyoutEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly newUserDetailsFlyout: boolean; readonly newUserDetailsFlyoutManagedUser: boolean; readonly newHostDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly sentinelOneDataInAnalyzerEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; readonly crowdstrikeDataInAnalyzerEnabled: boolean; readonly jamfDataInAnalyzerEnabled: boolean; readonly jsonPrebuiltRulesDiffingEnabled: boolean; readonly timelineEsqlTabDisabled: boolean; readonly unifiedComponentsInTimelineEnabled: boolean; readonly analyzerDatePickersAndSourcererDisabled: boolean; readonly perFieldPrebuiltRulesDiffingEnabled: boolean; readonly malwareOnWriteScanOptionAvailable: boolean; readonly aiAssistantFlyoutMode: boolean; readonly valueListItemsModalEnabled: boolean; readonly bulkCustomHighlightedFieldsEnabled: boolean; }" + "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly automatedProcessActionsEnabled: boolean; readonly responseActionsSentinelOneV1Enabled: boolean; readonly responseActionsSentinelOneV2Enabled: boolean; readonly responseActionsSentinelOneGetFileEnabled: boolean; readonly agentStatusClientEnabled: boolean; readonly responseActionsCrowdstrikeManualHostIsolationEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutInCreateRuleEnabled: boolean; readonly expandableEventFlyoutEnabled: boolean; readonly expandableTimelineFlyoutEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly newUserDetailsFlyout: boolean; readonly newUserDetailsFlyoutManagedUser: boolean; readonly newHostDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly sentinelOneDataInAnalyzerEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; readonly crowdstrikeDataInAnalyzerEnabled: boolean; readonly jamfDataInAnalyzerEnabled: boolean; readonly jsonPrebuiltRulesDiffingEnabled: boolean; readonly timelineEsqlTabDisabled: boolean; readonly unifiedComponentsInTimelineEnabled: boolean; readonly analyzerDatePickersAndSourcererDisabled: boolean; readonly perFieldPrebuiltRulesDiffingEnabled: boolean; readonly malwareOnWriteScanOptionAvailable: boolean; readonly unifiedManifestEnabled: boolean; readonly aiAssistantFlyoutMode: boolean; readonly valueListItemsModalEnabled: boolean; readonly bulkCustomHighlightedFieldsEnabled: boolean; }" ], "path": "x-pack/plugins/security_solution/common/experimental_features.ts", "deprecated": false, @@ -3296,7 +3296,7 @@ "\nA list of allowed values that can be used in `xpack.securitySolution.enableExperimental`.\nThis object is then used to validate and parse the value entered." ], "signature": [ - "{ readonly tGridEnabled: true; readonly tGridEventRenderedViewEnabled: true; readonly excludePoliciesInFilterEnabled: false; readonly kubernetesEnabled: true; readonly donutChartEmbeddablesEnabled: false; readonly previewTelemetryUrlEnabled: false; readonly insightsRelatedAlertsByProcessAncestry: true; readonly extendedRuleExecutionLoggingEnabled: false; readonly socTrendsEnabled: false; readonly responseActionsEnabled: true; readonly endpointResponseActionsEnabled: true; readonly responseActionUploadEnabled: true; readonly automatedProcessActionsEnabled: true; readonly responseActionsSentinelOneV1Enabled: true; readonly responseActionsSentinelOneV2Enabled: false; readonly responseActionsSentinelOneGetFileEnabled: false; readonly agentStatusClientEnabled: false; readonly responseActionsCrowdstrikeManualHostIsolationEnabled: false; readonly alertsPageChartsEnabled: true; readonly alertTypeEnabled: false; readonly expandableFlyoutInCreateRuleEnabled: true; readonly expandableEventFlyoutEnabled: true; readonly expandableTimelineFlyoutEnabled: true; readonly alertsPageFiltersEnabled: true; readonly assistantModelEvaluation: false; readonly newUserDetailsFlyout: true; readonly newUserDetailsFlyoutManagedUser: false; readonly newHostDetailsFlyout: true; readonly riskScoringPersistence: true; readonly riskScoringRoutesEnabled: true; readonly esqlRulesDisabled: false; readonly protectionUpdatesEnabled: true; readonly disableTimelineSaveTour: false; readonly riskEnginePrivilegesRouteEnabled: true; readonly sentinelOneDataInAnalyzerEnabled: true; readonly sentinelOneManualHostActionsEnabled: true; readonly crowdstrikeDataInAnalyzerEnabled: false; readonly jamfDataInAnalyzerEnabled: false; readonly jsonPrebuiltRulesDiffingEnabled: true; readonly timelineEsqlTabDisabled: false; readonly unifiedComponentsInTimelineEnabled: false; readonly analyzerDatePickersAndSourcererDisabled: false; readonly perFieldPrebuiltRulesDiffingEnabled: true; readonly malwareOnWriteScanOptionAvailable: false; readonly aiAssistantFlyoutMode: true; readonly valueListItemsModalEnabled: true; readonly bulkCustomHighlightedFieldsEnabled: false; }" + "{ readonly tGridEnabled: true; readonly tGridEventRenderedViewEnabled: true; readonly excludePoliciesInFilterEnabled: false; readonly kubernetesEnabled: true; readonly donutChartEmbeddablesEnabled: false; readonly previewTelemetryUrlEnabled: false; readonly insightsRelatedAlertsByProcessAncestry: true; readonly extendedRuleExecutionLoggingEnabled: false; readonly socTrendsEnabled: false; readonly responseActionsEnabled: true; readonly endpointResponseActionsEnabled: true; readonly responseActionUploadEnabled: true; readonly automatedProcessActionsEnabled: true; readonly responseActionsSentinelOneV1Enabled: true; readonly responseActionsSentinelOneV2Enabled: false; readonly responseActionsSentinelOneGetFileEnabled: false; readonly agentStatusClientEnabled: false; readonly responseActionsCrowdstrikeManualHostIsolationEnabled: false; readonly alertsPageChartsEnabled: true; readonly alertTypeEnabled: false; readonly expandableFlyoutInCreateRuleEnabled: true; readonly expandableEventFlyoutEnabled: true; readonly expandableTimelineFlyoutEnabled: true; readonly alertsPageFiltersEnabled: true; readonly assistantModelEvaluation: false; readonly newUserDetailsFlyout: true; readonly newUserDetailsFlyoutManagedUser: false; readonly newHostDetailsFlyout: true; readonly riskScoringPersistence: true; readonly riskScoringRoutesEnabled: true; readonly esqlRulesDisabled: false; readonly protectionUpdatesEnabled: true; readonly disableTimelineSaveTour: false; readonly riskEnginePrivilegesRouteEnabled: true; readonly sentinelOneDataInAnalyzerEnabled: true; readonly sentinelOneManualHostActionsEnabled: true; readonly crowdstrikeDataInAnalyzerEnabled: false; readonly jamfDataInAnalyzerEnabled: false; readonly jsonPrebuiltRulesDiffingEnabled: true; readonly timelineEsqlTabDisabled: false; readonly unifiedComponentsInTimelineEnabled: false; readonly analyzerDatePickersAndSourcererDisabled: false; readonly perFieldPrebuiltRulesDiffingEnabled: true; readonly malwareOnWriteScanOptionAvailable: true; readonly unifiedManifestEnabled: false; readonly aiAssistantFlyoutMode: true; readonly valueListItemsModalEnabled: true; readonly bulkCustomHighlightedFieldsEnabled: false; }" ], "path": "x-pack/plugins/security_solution/common/experimental_features.ts", "deprecated": false, diff --git a/api_docs/security_solution.mdx b/api_docs/security_solution.mdx index 85c8a7e004df7..27007122fc6d8 100644 --- a/api_docs/security_solution.mdx +++ b/api_docs/security_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolution title: "securitySolution" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolution plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; diff --git a/api_docs/security_solution_ess.mdx b/api_docs/security_solution_ess.mdx index fea3359dc12e8..811a8cda4699f 100644 --- a/api_docs/security_solution_ess.mdx +++ b/api_docs/security_solution_ess.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolutionEss title: "securitySolutionEss" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolutionEss plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolutionEss'] --- import securitySolutionEssObj from './security_solution_ess.devdocs.json'; diff --git a/api_docs/security_solution_serverless.mdx b/api_docs/security_solution_serverless.mdx index d6ddfb15b8aa3..cc23b3a3c1587 100644 --- a/api_docs/security_solution_serverless.mdx +++ b/api_docs/security_solution_serverless.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolutionServerless title: "securitySolutionServerless" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolutionServerless plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolutionServerless'] --- import securitySolutionServerlessObj from './security_solution_serverless.devdocs.json'; diff --git a/api_docs/serverless.mdx b/api_docs/serverless.mdx index 30cbb8e5ac8a7..c0af522c9791d 100644 --- a/api_docs/serverless.mdx +++ b/api_docs/serverless.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverless title: "serverless" image: https://source.unsplash.com/400x175/?github description: API docs for the serverless plugin -date: 2024-05-16 +date: 2024-05-20 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 cf0a98ae88b87..d81adb6537e73 100644 --- a/api_docs/serverless_observability.mdx +++ b/api_docs/serverless_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessObservability title: "serverlessObservability" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessObservability plugin -date: 2024-05-16 +date: 2024-05-20 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 1cb1741ffda15..61c74b672b4e7 100644 --- a/api_docs/serverless_search.mdx +++ b/api_docs/serverless_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessSearch title: "serverlessSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessSearch plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverlessSearch'] --- import serverlessSearchObj from './serverless_search.devdocs.json'; diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx index c1f4d9050b1ef..ea83468fcd1d3 100644 --- a/api_docs/session_view.mdx +++ b/api_docs/session_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/sessionView title: "sessionView" image: https://source.unsplash.com/400x175/?github description: API docs for the sessionView plugin -date: 2024-05-16 +date: 2024-05-20 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 8c720971ed0a1..8e94973d5c349 100644 --- a/api_docs/share.mdx +++ b/api_docs/share.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/share title: "share" image: https://source.unsplash.com/400x175/?github description: API docs for the share plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'share'] --- import shareObj from './share.devdocs.json'; diff --git a/api_docs/slo.devdocs.json b/api_docs/slo.devdocs.json index 7641ca4b43fd5..02cfed22cb843 100644 --- a/api_docs/slo.devdocs.json +++ b/api_docs/slo.devdocs.json @@ -1256,7 +1256,7 @@ }, "<", "CreateSLOForm", - "<{ type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"last_value\" | \"cardinality\" | \"std_deviation\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }; }>> | undefined; }) => Promise; }" + "<{ type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"last_value\" | \"cardinality\" | \"std_deviation\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }; }>> | undefined; }) => JSX.Element; }" ], "path": "x-pack/plugins/observability_solution/slo/public/types.ts", "deprecated": false, diff --git a/api_docs/slo.mdx b/api_docs/slo.mdx index 8b5389648d7be..7ef4a611abcf8 100644 --- a/api_docs/slo.mdx +++ b/api_docs/slo.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/slo title: "slo" image: https://source.unsplash.com/400x175/?github description: API docs for the slo plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'slo'] --- import sloObj from './slo.devdocs.json'; diff --git a/api_docs/snapshot_restore.mdx b/api_docs/snapshot_restore.mdx index f6aaf051b3b9a..0ea1d3172dd3b 100644 --- a/api_docs/snapshot_restore.mdx +++ b/api_docs/snapshot_restore.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/snapshotRestore title: "snapshotRestore" image: https://source.unsplash.com/400x175/?github description: API docs for the snapshotRestore plugin -date: 2024-05-16 +date: 2024-05-20 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 830a9e5aca980..087542719d1e4 100644 --- a/api_docs/spaces.mdx +++ b/api_docs/spaces.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/spaces title: "spaces" image: https://source.unsplash.com/400x175/?github description: API docs for the spaces plugin -date: 2024-05-16 +date: 2024-05-20 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 812a693b535ed..1a2bbb0524a15 100644 --- a/api_docs/stack_alerts.mdx +++ b/api_docs/stack_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackAlerts title: "stackAlerts" image: https://source.unsplash.com/400x175/?github description: API docs for the stackAlerts plugin -date: 2024-05-16 +date: 2024-05-20 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 02eb6962b84dd..33fc2ffd33a4e 100644 --- a/api_docs/stack_connectors.mdx +++ b/api_docs/stack_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackConnectors title: "stackConnectors" image: https://source.unsplash.com/400x175/?github description: API docs for the stackConnectors plugin -date: 2024-05-16 +date: 2024-05-20 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 516c11d380aa9..6e2fc9a19eaf8 100644 --- a/api_docs/task_manager.mdx +++ b/api_docs/task_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/taskManager title: "taskManager" image: https://source.unsplash.com/400x175/?github description: API docs for the taskManager plugin -date: 2024-05-16 +date: 2024-05-20 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 74631a8919b1e..79de411a6e575 100644 --- a/api_docs/telemetry.mdx +++ b/api_docs/telemetry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetry title: "telemetry" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetry plugin -date: 2024-05-16 +date: 2024-05-20 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 053edf9c9126d..95d3eec19eba4 100644 --- a/api_docs/telemetry_collection_manager.mdx +++ b/api_docs/telemetry_collection_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionManager title: "telemetryCollectionManager" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionManager plugin -date: 2024-05-16 +date: 2024-05-20 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 b90952aa40d1d..ec85be98e62b7 100644 --- a/api_docs/telemetry_collection_xpack.mdx +++ b/api_docs/telemetry_collection_xpack.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionXpack title: "telemetryCollectionXpack" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionXpack plugin -date: 2024-05-16 +date: 2024-05-20 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 857fc60f97275..6531727d0d0fc 100644 --- a/api_docs/telemetry_management_section.mdx +++ b/api_docs/telemetry_management_section.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryManagementSection title: "telemetryManagementSection" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryManagementSection plugin -date: 2024-05-16 +date: 2024-05-20 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 8ecaf47a13738..e5df0709fea7d 100644 --- a/api_docs/text_based_languages.mdx +++ b/api_docs/text_based_languages.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/textBasedLanguages title: "textBasedLanguages" image: https://source.unsplash.com/400x175/?github description: API docs for the textBasedLanguages plugin -date: 2024-05-16 +date: 2024-05-20 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 378d0497d9775..8a58fa683984b 100644 --- a/api_docs/threat_intelligence.mdx +++ b/api_docs/threat_intelligence.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/threatIntelligence title: "threatIntelligence" image: https://source.unsplash.com/400x175/?github description: API docs for the threatIntelligence plugin -date: 2024-05-16 +date: 2024-05-20 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 e1022f8e64e82..207e867788d38 100644 --- a/api_docs/timelines.mdx +++ b/api_docs/timelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/timelines title: "timelines" image: https://source.unsplash.com/400x175/?github description: API docs for the timelines plugin -date: 2024-05-16 +date: 2024-05-20 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 9bda799d01487..77864ff7ffdb3 100644 --- a/api_docs/transform.mdx +++ b/api_docs/transform.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/transform title: "transform" image: https://source.unsplash.com/400x175/?github description: API docs for the transform plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'transform'] --- import transformObj from './transform.devdocs.json'; diff --git a/api_docs/triggers_actions_ui.mdx b/api_docs/triggers_actions_ui.mdx index 4ff64d38543aa..ecceb395dcf5d 100644 --- a/api_docs/triggers_actions_ui.mdx +++ b/api_docs/triggers_actions_ui.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/triggersActionsUi title: "triggersActionsUi" image: https://source.unsplash.com/400x175/?github description: API docs for the triggersActionsUi plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'triggersActionsUi'] --- import triggersActionsUiObj from './triggers_actions_ui.devdocs.json'; diff --git a/api_docs/ui_actions.mdx b/api_docs/ui_actions.mdx index 709beb47f717d..b561d2da43a8e 100644 --- a/api_docs/ui_actions.mdx +++ b/api_docs/ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActions title: "uiActions" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActions plugin -date: 2024-05-16 +date: 2024-05-20 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 3a0eae1a653d8..7522b26b6f3f5 100644 --- a/api_docs/ui_actions_enhanced.mdx +++ b/api_docs/ui_actions_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActionsEnhanced title: "uiActionsEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActionsEnhanced plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.devdocs.json'; diff --git a/api_docs/unified_doc_viewer.mdx b/api_docs/unified_doc_viewer.mdx index 73a64d8061abd..c1f174a432bb2 100644 --- a/api_docs/unified_doc_viewer.mdx +++ b/api_docs/unified_doc_viewer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedDocViewer title: "unifiedDocViewer" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedDocViewer plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedDocViewer'] --- import unifiedDocViewerObj from './unified_doc_viewer.devdocs.json'; diff --git a/api_docs/unified_histogram.mdx b/api_docs/unified_histogram.mdx index 69324711e559b..074ba92429cbe 100644 --- a/api_docs/unified_histogram.mdx +++ b/api_docs/unified_histogram.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedHistogram title: "unifiedHistogram" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedHistogram plugin -date: 2024-05-16 +date: 2024-05-20 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 4f96aa3a99e84..0ef774e7bf6be 100644 --- a/api_docs/unified_search.mdx +++ b/api_docs/unified_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch title: "unifiedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch plugin -date: 2024-05-16 +date: 2024-05-20 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 22caefb2c4aa7..b79b3c80e18e6 100644 --- a/api_docs/unified_search_autocomplete.mdx +++ b/api_docs/unified_search_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch-autocomplete title: "unifiedSearch.autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch.autocomplete plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete'] --- import unifiedSearchAutocompleteObj from './unified_search_autocomplete.devdocs.json'; diff --git a/api_docs/uptime.mdx b/api_docs/uptime.mdx index 75c3728f3990a..db3a5756af139 100644 --- a/api_docs/uptime.mdx +++ b/api_docs/uptime.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uptime title: "uptime" image: https://source.unsplash.com/400x175/?github description: API docs for the uptime plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uptime'] --- import uptimeObj from './uptime.devdocs.json'; diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index 89fb7f8808360..d7b26a682cb27 100644 --- a/api_docs/url_forwarding.mdx +++ b/api_docs/url_forwarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/urlForwarding title: "urlForwarding" image: https://source.unsplash.com/400x175/?github description: API docs for the urlForwarding plugin -date: 2024-05-16 +date: 2024-05-20 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 45c0fe42cf6ce..e741e98b12d20 100644 --- a/api_docs/usage_collection.mdx +++ b/api_docs/usage_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/usageCollection title: "usageCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the usageCollection plugin -date: 2024-05-16 +date: 2024-05-20 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 e53fe6a4b37c8..5bca97e899953 100644 --- a/api_docs/ux.mdx +++ b/api_docs/ux.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ux title: "ux" image: https://source.unsplash.com/400x175/?github description: API docs for the ux plugin -date: 2024-05-16 +date: 2024-05-20 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 17e298249be94..193e96d59bb23 100644 --- a/api_docs/vis_default_editor.mdx +++ b/api_docs/vis_default_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visDefaultEditor title: "visDefaultEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the visDefaultEditor plugin -date: 2024-05-16 +date: 2024-05-20 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 f3adce8e310ca..7d5ef7ea92dff 100644 --- a/api_docs/vis_type_gauge.mdx +++ b/api_docs/vis_type_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeGauge title: "visTypeGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeGauge plugin -date: 2024-05-16 +date: 2024-05-20 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 5d6141e96a9cf..dab1277709244 100644 --- a/api_docs/vis_type_heatmap.mdx +++ b/api_docs/vis_type_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeHeatmap title: "visTypeHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeHeatmap plugin -date: 2024-05-16 +date: 2024-05-20 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 2696af2b3e9b5..496bb0951f15c 100644 --- a/api_docs/vis_type_pie.mdx +++ b/api_docs/vis_type_pie.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypePie title: "visTypePie" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypePie plugin -date: 2024-05-16 +date: 2024-05-20 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 349085d407f44..9bb57b81870b0 100644 --- a/api_docs/vis_type_table.mdx +++ b/api_docs/vis_type_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTable title: "visTypeTable" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTable plugin -date: 2024-05-16 +date: 2024-05-20 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 b97fa189cac8b..0607b3fbe497e 100644 --- a/api_docs/vis_type_timelion.mdx +++ b/api_docs/vis_type_timelion.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimelion title: "visTypeTimelion" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimelion plugin -date: 2024-05-16 +date: 2024-05-20 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 ad4855319ba27..0ff4a1b9f893a 100644 --- a/api_docs/vis_type_timeseries.mdx +++ b/api_docs/vis_type_timeseries.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimeseries title: "visTypeTimeseries" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimeseries plugin -date: 2024-05-16 +date: 2024-05-20 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 62ca2ea357824..ebc6df00a2798 100644 --- a/api_docs/vis_type_vega.mdx +++ b/api_docs/vis_type_vega.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVega title: "visTypeVega" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVega plugin -date: 2024-05-16 +date: 2024-05-20 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 9be604bdcc0da..be3ee2bb95a9b 100644 --- a/api_docs/vis_type_vislib.mdx +++ b/api_docs/vis_type_vislib.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVislib title: "visTypeVislib" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVislib plugin -date: 2024-05-16 +date: 2024-05-20 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 31bdcc7b53bb3..b120e745b9875 100644 --- a/api_docs/vis_type_xy.mdx +++ b/api_docs/vis_type_xy.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeXy title: "visTypeXy" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeXy plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeXy'] --- import visTypeXyObj from './vis_type_xy.devdocs.json'; diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index a33529abce609..4c015585d1a70 100644 --- a/api_docs/visualizations.mdx +++ b/api_docs/visualizations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visualizations title: "visualizations" image: https://source.unsplash.com/400x175/?github description: API docs for the visualizations plugin -date: 2024-05-16 +date: 2024-05-20 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; diff --git a/docs/concepts/esql.asciidoc b/docs/concepts/esql.asciidoc index 97f4b82586322..0390f9f6e2bc7 100644 --- a/docs/concepts/esql.asciidoc +++ b/docs/concepts/esql.asciidoc @@ -1,23 +1,36 @@ [[esql]] === {esql} -preview::["Do not use {esql} on production environments. This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features."] +The Elasticsearch Query Language, {esql}, makes it faster and easier to explore your data. -The Elasticsearch Query Language, {esql}, has been created to make exploring your data faster and easier using the **Discover** application. From version 8.11 you can try this new feature, which is enabled by default. +{esql} is a piped language which allows you to chain together multiple commands to query your data. +Based on the query, Lens suggestions in Discover create a visualization of the query results. -[role="screenshot"] -image:images/esql-data-view-menu.png[An image of the Discover UI where users can access the {esql} feature, width=30%] +{esql} comes with its own dedicated {esql} Compute Engine for greater efficiency. With one query you can search, aggregate, calculate and perform data transformations without leaving **Discover**. Write your query directly in **Discover** or use the **Dev Tools** with the {ref}/esql-rest.html[{esql} API]. -This new piped language allows you to chain together multiple commands to query your data. Based on the query, Lens suggestions in Discover create a visualization of the query results. +Here's how to use {esql} in the data view selector in **Discover**: -{esql} comes with its own dedicated {esql} Compute Engine for greater efficiency. From one query you can search, aggregate, calculate and perform data transformations without leaving **Discover**. Write your query directly in **Discover** or use the **Dev Tools** with the {ref}/esql-rest.html[{esql} API]. +[role="screenshot"] +image:images/esql-data-view-menu.png[An image of the Discover UI where users can access the {esql} feature, width=30%, align="center"] {esql} also features in-app help, so you can get started faster and don't have to leave the application to check syntax. [role="screenshot"] image:images/esql-in-app-help.png[An image of the Discover UI where users can browse the in-app help] -For more detailed information about the {esql} language, refer to {ref}/esql-language.html[Learning {esql}]. +You can also use ES|QL queries to create panels on your dashboards, create enrich policies, and create alerting rules. + +For more detailed information about {esql} in Kibana, refer to {ref}/esql-kibana.html[Using {esql} in {kib}]. + +[NOTE] +==== +{esql} is enabled by default in {kib}. It can be +disabled using the `enableESQL` setting from the +{kibana-ref}/advanced-options.html[Advanced Settings]. + +This will hide the {esql} user interface from various applications. +However, users will be able to access existing {esql} artifacts like saved searches and visualizations. +==== [float] [[esql-observability]] @@ -35,6 +48,6 @@ Use {esql} to retrieve important information for investigation by using lookups. [[esql-whats-next]] ==== What's next? -Full documentation for this language is available in the {es} documentation, refer to {ref}/esql.html[{esql}]. +The main documentation for {esql} lives in the {ref}/esql.html[{es} docs]. -Alternatively, a short tutorial is available in the **Discover** section <>. \ No newline at end of file +We also have a short tutorial in the **Discover** docs: <>. \ No newline at end of file diff --git a/docs/concepts/images/esql-data-view-menu.png b/docs/concepts/images/esql-data-view-menu.png index fbbbdf44d315c..15e7365626ba8 100644 Binary files a/docs/concepts/images/esql-data-view-menu.png and b/docs/concepts/images/esql-data-view-menu.png differ diff --git a/docs/concepts/images/esql-in-app-help.png b/docs/concepts/images/esql-in-app-help.png index eb818a11cbacb..5f00248c10af2 100644 Binary files a/docs/concepts/images/esql-in-app-help.png and b/docs/concepts/images/esql-in-app-help.png differ diff --git a/docs/discover/try-esql.asciidoc b/docs/discover/try-esql.asciidoc index 7731700147e50..32718d87c955a 100644 --- a/docs/discover/try-esql.asciidoc +++ b/docs/discover/try-esql.asciidoc @@ -1,5 +1,5 @@ [[try-esql]] -== Try {esql} +== Using {esql} The Elasticsearch Query Language, {esql}, makes it easier to explore your data without leaving Discover. @@ -9,11 +9,11 @@ In this tutorial we'll use the {kib} sample web logs in Discover and Lens to exp [[prerequisite]] === Prerequisite -To be able to select **Try {esql}** from the Data views menu the `enableESQL` setting must be enabled from **Stack Management > Advanced Settings**. It is enabled by default. +To be able to select **Language {esql}** from the Data views menu the `enableESQL` setting must be enabled from **Stack Management > Advanced Settings**. It is enabled by default. [float] [[tutorial-try-esql]] -=== Trying {esql} +=== Use {esql} To load the sample data: @@ -21,7 +21,7 @@ To load the sample data: . Click **Other sample data sets**. . On the Sample web logs card, click **Add data**. . Open the main menu and select *Discover*. -. From the Data views menu, select *Try {esql}*. +. From the Data views menu, select *Language {esql}*. Let's say we want to find out what operating system users have and how much RAM is on their machine. @@ -36,7 +36,7 @@ FROM kibana_sample_data_logs | KEEP machine.os, machine.ram ---- + -. Click **Update**. +. Click **▶Run**. + [role="screenshot"] image:images/esql-machine-os-ram.png[An image of the query result] @@ -57,7 +57,7 @@ FROM kibana_sample_data_logs | LIMIT 10 ---- + -. Click **Update**. +. Click **▶Run**. + [role="screenshot"] image:images/esql-limit.png[An image of the extended query result] @@ -75,7 +75,7 @@ FROM kibana_sample_data_logs | LIMIT 10 ---- + -. Click **Update**. +. Click **▶Run**. + [role="screenshot"] image:images/esql-full-query.png[] @@ -84,6 +84,9 @@ image:images/esql-full-query.png[] To make changes to the visualization you can use the visualization drop-down. To make changes to the colors used or the axes, or click the pencil icon. This opens an in-line editor where you can change the colors and axes of the visualization. -To learn more about {esql}, try other tutorials, see more examples and reference material, refer to {ref}/esql.html[{esql}]. - +[TIP] +==== +For the complete {esql} documentation, including tutorials, examples and the full syntax reference, refer to the {ref}/esql.html[{es} documentation]. +For a more detailed overview of {esql} in {kib}, refer to {ref}/esql-kibana.html[Use {esql} in Kibana]. +==== diff --git a/package.json b/package.json index dd5fbd1c06325..d91a1752ead8e 100644 --- a/package.json +++ b/package.json @@ -83,9 +83,7 @@ "**/@langchain/core": "0.1.53", "**/@types/node": "20.10.5", "**/@typescript-eslint/utils": "5.62.0", - "**/axios": "1.6.3", "**/chokidar": "^3.5.3", - "**/follow-redirects": "1.15.2", "**/globule/minimatch": "^3.1.2", "**/hoist-non-react-statics": "^3.3.2", "**/isomorphic-fetch/node-fetch": "^2.6.7", @@ -109,7 +107,7 @@ "@elastic/ecs": "^8.11.1", "@elastic/elasticsearch": "^8.13.0", "@elastic/ems-client": "8.5.1", - "@elastic/eui": "94.3.0", + "@elastic/eui": "94.5.0-backport.1", "@elastic/filesaver": "1.1.2", "@elastic/node-crypto": "1.2.1", "@elastic/numeral": "^2.5.1", @@ -441,6 +439,7 @@ "@kbn/embedded-lens-example-plugin": "link:x-pack/examples/embedded_lens_example", "@kbn/encrypted-saved-objects-plugin": "link:x-pack/plugins/encrypted_saved_objects", "@kbn/enterprise-search-plugin": "link:x-pack/plugins/enterprise_search", + "@kbn/entities-schema": "link:x-pack/packages/kbn-entities-schema", "@kbn/error-boundary-example-plugin": "link:examples/error_boundary", "@kbn/es-errors": "link:packages/kbn-es-errors", "@kbn/es-query": "link:packages/kbn-es-query", @@ -959,7 +958,7 @@ "archiver": "^5.3.1", "async": "^3.2.3", "aws4": "^1.12.0", - "axios": "1.6.3", + "axios": "^1.6.8", "base64-js": "^1.3.1", "bitmap-sdf": "^1.0.3", "blurhash": "^2.0.1", @@ -1525,7 +1524,7 @@ "@types/source-map-support": "^0.5.3", "@types/stats-lite": "^2.2.0", "@types/styled-components": "^5.1.0", - "@types/supertest": "^2.0.12", + "@types/supertest": "^6.0.2", "@types/tapable": "^1.0.6", "@types/tar": "^6.1.11", "@types/testing-library__jest-dom": "^5.14.7", @@ -1580,7 +1579,7 @@ "cssnano": "^5.1.12", "cssnano-preset-default": "^5.2.12", "csstype": "^3.0.2", - "cypress": "13.6.2", + "cypress": "13.6.3", "cypress-axe": "^1.5.0", "cypress-file-upload": "^5.0.8", "cypress-multi-reporters": "^1.6.4", @@ -1699,8 +1698,8 @@ "style-loader": "^1.1.3", "stylelint": "^14.9.1", "stylelint-scss": "^4.3.0", - "superagent": "^8.1.2", - "supertest": "^6.3.3", + "superagent": "^9.0.2", + "supertest": "^7.0.0", "svgo": "^2.8.0", "table": "^6.8.1", "tape": "^5.0.1", diff --git a/packages/cloud/connection_details/components/copy_input/copy_input.tsx b/packages/cloud/connection_details/components/copy_input/copy_input.tsx index 6111208b06148..ac82407fe1588 100644 --- a/packages/cloud/connection_details/components/copy_input/copy_input.tsx +++ b/packages/cloud/connection_details/components/copy_input/copy_input.tsx @@ -12,9 +12,10 @@ import { i18n } from '@kbn/i18n'; export interface CopyInputProps { value: string; + onCopyClick?: React.MouseEventHandler; } -export const CopyInput: React.FC = ({ value }) => { +export const CopyInput: React.FC = ({ value, onCopyClick }) => { const textRef = React.useRef(null); return ( @@ -52,7 +53,10 @@ export const CopyInput: React.FC = ({ value }) => { {(copy) => ( ) => { + onCopyClick?.(event); + copy(); + }} iconType="copyClipboard" size="m" color={'text'} diff --git a/packages/cloud/connection_details/connection_details_flyout_content.stories.tsx b/packages/cloud/connection_details/connection_details_flyout_content.stories.tsx index 6893acfd8b8b3..542a9f343691b 100644 --- a/packages/cloud/connection_details/connection_details_flyout_content.stories.tsx +++ b/packages/cloud/connection_details/connection_details_flyout_content.stories.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { EuiFlyout } from '@elastic/eui'; +import { action } from '@storybook/addon-actions'; import { StoriesProvider, StoriesProviderKeyCreationError, @@ -22,7 +23,7 @@ export default { export const Default = () => { return ( {}}> - + diff --git a/packages/cloud/connection_details/connection_details_flyout_content.tsx b/packages/cloud/connection_details/connection_details_flyout_content.tsx index 9cbe8042caa42..31dce6dc4f848 100644 --- a/packages/cloud/connection_details/connection_details_flyout_content.tsx +++ b/packages/cloud/connection_details/connection_details_flyout_content.tsx @@ -17,11 +17,12 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { ConnectionDetails } from './connection_details'; -import { useConnectionDetailsOpts } from './context'; +import { useConnectionDetailsOpts, useConnectionDetailsService } from './context'; import { Tabs } from './tabs'; export const ConnectionDetailsFlyoutContent: React.FC = () => { const ctx = useConnectionDetailsOpts(); + const service = useConnectionDetailsService(); const header = ( @@ -39,7 +40,15 @@ export const ConnectionDetailsFlyoutContent: React.FC = () => { defaultMessage: 'Connect to the Elasticsearch API by using the following details.', })}{' '} {!!ctx.links?.learnMore && ( - + // Below onClick is used only for telemetry, but `href` is the real + // semantic action. + // eslint-disable-next-line @elastic/eui/href-or-on-click + service.emitTelemetryEvent(['learn_more_clicked'])} + > {i18n.translate('cloud.connectionDetails.learnMoreButtonLabel', { defaultMessage: 'Learn more', })} diff --git a/packages/cloud/connection_details/kibana/global.ts b/packages/cloud/connection_details/kibana/global.ts index af3767d12ad77..0a2ede8e5157d 100644 --- a/packages/cloud/connection_details/kibana/global.ts +++ b/packages/cloud/connection_details/kibana/global.ts @@ -19,6 +19,7 @@ export interface ConnectionDetailsGlobalDependencies { http: CoreStart['http']; application: CoreStart['application']; overlays: CoreStart['overlays']; + analytics?: CoreStart['analytics']; }; plugins: { cloud?: CloudStart; diff --git a/packages/cloud/connection_details/kibana/kibana_connection_details_provider.tsx b/packages/cloud/connection_details/kibana/kibana_connection_details_provider.tsx index 38bc354e9c912..a8cc79dfcd9d2 100644 --- a/packages/cloud/connection_details/kibana/kibana_connection_details_provider.tsx +++ b/packages/cloud/connection_details/kibana/kibana_connection_details_provider.tsx @@ -17,7 +17,7 @@ import { useAsyncMemo } from '../hooks/use_async_memo'; const createOpts = async (props: KibanaConnectionDetailsProviderProps) => { const { options, start } = props; - const { http, docLinks } = start.core; + const { http, docLinks, analytics } = start.core; const locator = start.plugins?.share?.url?.locators.get('MANAGEMENT_APP_LOCATOR'); const manageKeysLink = await locator?.getUrl({ sectionId: 'security', appId: 'api_keys' }); const result: ConnectionDetailsOpts = { @@ -68,6 +68,51 @@ const createOpts = async (props: KibanaConnectionDetailsProviderProps) => { hasPermission: async () => true, ...options?.apiKeys, }, + onTelemetryEvent: (event) => { + if (!analytics) return; + switch (event[0]) { + case 'learn_more_clicked': { + analytics.reportEvent('connection_details_learn_more_clicked', {}); + break; + } + case 'tab_switched': { + analytics.reportEvent('connection_details_tab_switched', { tab: event[1]!.tab }); + break; + } + case 'copy_endpoint_url_clicked': { + analytics.reportEvent('connection_details_copy_endpoint_url_clicked', {}); + break; + } + case 'show_cloud_id_toggled': { + analytics.reportEvent('connection_details_show_cloud_id_toggled', {}); + break; + } + case 'copy_cloud_id_clicked': { + analytics.reportEvent('connection_details_copy_cloud_id_clicked', {}); + break; + } + case 'new_api_key_created': { + analytics.reportEvent('connection_details_new_api_key_created', {}); + break; + } + case 'manage_api_keys_clicked': { + analytics.reportEvent('connection_details_manage_api_keys_clicked', {}); + break; + } + case 'key_encoding_changed': { + analytics.reportEvent('connection_details_key_encoding_changed', { + format: event[1]!.format, + }); + break; + } + case 'copy_api_key_clicked': { + analytics.reportEvent('connection_details_copy_api_key_clicked', { + format: event[1]!.format, + }); + break; + } + } + }, }; return result; @@ -83,6 +128,7 @@ export interface KibanaConnectionDetailsProviderProps { theme: CoreStart['theme']; http?: CoreStart['http']; application?: CoreStart['application']; + analytics?: CoreStart['analytics']; }; plugins?: { cloud?: CloudStart; diff --git a/packages/cloud/connection_details/service.ts b/packages/cloud/connection_details/service.ts index 1e59206337baa..f07a3edeae201 100644 --- a/packages/cloud/connection_details/service.ts +++ b/packages/cloud/connection_details/service.ts @@ -10,7 +10,7 @@ import { BehaviorSubject } from 'rxjs'; import { i18n } from '@kbn/i18n'; import { ApiKey } from './tabs/api_keys_tab/views/success_form/types'; import type { Format } from './tabs/api_keys_tab/views/success_form/format_select'; -import type { ConnectionDetailsOpts, TabID } from './types'; +import type { ConnectionDetailsOpts, TabID, ConnectionDetailsTelemetryEvents } from './types'; export class ConnectionDetailsService { public readonly tabId$ = new BehaviorSubject('endpoints'); @@ -39,6 +39,7 @@ export class ConnectionDetailsService { }; public readonly toggleShowCloudId = () => { + this.emitTelemetryEvent(['show_cloud_id_toggled']); this.showCloudId$.next(!this.showCloudId$.getValue()); }; @@ -48,6 +49,7 @@ export class ConnectionDetailsService { }; public readonly setApiKeyFormat = (format: Format) => { + this.emitTelemetryEvent(['key_encoding_changed', { format }]); this.apiKeyFormat$.next(format); }; @@ -76,6 +78,7 @@ export class ConnectionDetailsService { name: this.apiKeyName$.getValue(), }); this.apiKey$.next(apiKey); + this.emitTelemetryEvent(['new_api_key_created']); } catch (error) { this.apiKeyError$.next(error); } finally { @@ -88,4 +91,13 @@ export class ConnectionDetailsService { this.apiKeyError$.next(error); }); }; + + public readonly emitTelemetryEvent = (event: ConnectionDetailsTelemetryEvents) => { + try { + this.opts.onTelemetryEvent?.(event); + } catch (error) { + // eslint-disable-next-line no-console + console.error('Error emitting telemetry event', error); + } + }; } diff --git a/packages/cloud/connection_details/stories.tsx b/packages/cloud/connection_details/stories.tsx index f6d15f181835c..41fb267f797e4 100644 --- a/packages/cloud/connection_details/stories.tsx +++ b/packages/cloud/connection_details/stories.tsx @@ -41,8 +41,15 @@ const defaultOpts: ConnectionDetailsOpts = { }, }; -export const StoriesProvider: React.FC = ({ children }) => { - return {children}; +export const StoriesProvider: React.FC> = ({ + children, + ...rest +}) => { + return ( + + {children} + + ); }; export const StoriesProviderKeyCreationError: React.FC = ({ children }) => { diff --git a/packages/cloud/connection_details/tabs/api_keys_tab/components/manage_keys_link.tsx b/packages/cloud/connection_details/tabs/api_keys_tab/components/manage_keys_link.tsx index a6ead0f520bb4..44ec35154874b 100644 --- a/packages/cloud/connection_details/tabs/api_keys_tab/components/manage_keys_link.tsx +++ b/packages/cloud/connection_details/tabs/api_keys_tab/components/manage_keys_link.tsx @@ -21,7 +21,10 @@ export const ManageKeysLink: React.FC = () => { return ( { + service.emitTelemetryEvent(['manage_api_keys_clicked']); + service.opts?.navigateToUrl?.(link); + }} data-test-subj={'connectionDetailsManageApiKeysLink'} > {i18n.translate('cloud.connectionDetails.apiKeys.managerLinkLabel', { diff --git a/packages/cloud/connection_details/tabs/api_keys_tab/views/missing_permissions_panel.tsx b/packages/cloud/connection_details/tabs/api_keys_tab/views/missing_permissions_panel.tsx index 5e2d5dd2da830..fcc58d14443c7 100644 --- a/packages/cloud/connection_details/tabs/api_keys_tab/views/missing_permissions_panel.tsx +++ b/packages/cloud/connection_details/tabs/api_keys_tab/views/missing_permissions_panel.tsx @@ -7,25 +7,32 @@ */ import * as React from 'react'; -import { EuiCallOut } from '@elastic/eui'; +import { EuiCallOut, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { ManageKeysLink } from '../components/manage_keys_link'; export const MissingPermissionsPanel: React.FC = () => { return ( - -

- {i18n.translate('cloud.connectionDetails.tabs.apiKeys.missingPermPanel.description', { - defaultMessage: - 'Your assigned role does not have the necessary permissions to create an API key. ' + - 'Please contact your administrator.', + <> + - + > +

+ {i18n.translate('cloud.connectionDetails.tabs.apiKeys.missingPermPanel.description', { + defaultMessage: + 'Your assigned role does not have the necessary permissions to create an API key. ' + + 'Please contact your administrator.', + })} +

+
+ + + + + ); }; diff --git a/packages/cloud/connection_details/tabs/api_keys_tab/views/success_form/success_form.tsx b/packages/cloud/connection_details/tabs/api_keys_tab/views/success_form/success_form.tsx index 154c3c8d2b4bb..f08ab3aec8844 100644 --- a/packages/cloud/connection_details/tabs/api_keys_tab/views/success_form/success_form.tsx +++ b/packages/cloud/connection_details/tabs/api_keys_tab/views/success_form/success_form.tsx @@ -23,6 +23,7 @@ export const SuccessForm: React.FC = () => { apiKey={apiKey} format={format} onFormatChange={service.setApiKeyFormat} + onCopyClick={() => service.emitTelemetryEvent(['copy_api_key_clicked', { format }])} /> ); }; diff --git a/packages/cloud/connection_details/tabs/api_keys_tab/views/success_form/success_form_controlled.tsx b/packages/cloud/connection_details/tabs/api_keys_tab/views/success_form/success_form_controlled.tsx index d5717bee8e244..e4dca6c5fe4f4 100644 --- a/packages/cloud/connection_details/tabs/api_keys_tab/views/success_form/success_form_controlled.tsx +++ b/packages/cloud/connection_details/tabs/api_keys_tab/views/success_form/success_form_controlled.tsx @@ -18,12 +18,14 @@ export interface SuccessFormControlledProps { apiKey: ApiKey; format: Format; onFormatChange: (format: Format) => void; + onCopyClick?: () => void; } export const SuccessFormControlled: React.FC = ({ apiKey, format, onFormatChange, + onCopyClick, }) => { const keyValue = format === 'encoded' ? apiKey.encoded : `${apiKey.id}:${apiKey.key}`; @@ -62,7 +64,7 @@ export const SuccessFormControlled: React.FC = ({ fullWidth data-test-subj={'connectionDetailsApiKeyValueRow'} > - + diff --git a/packages/cloud/connection_details/tabs/endpoints_tab/endpoints_tab.tsx b/packages/cloud/connection_details/tabs/endpoints_tab/endpoints_tab.tsx index 40f442348acf7..ffdea170096ac 100644 --- a/packages/cloud/connection_details/tabs/endpoints_tab/endpoints_tab.tsx +++ b/packages/cloud/connection_details/tabs/endpoints_tab/endpoints_tab.tsx @@ -8,19 +8,35 @@ import { EuiForm } from '@elastic/eui'; import * as React from 'react'; -import { useConnectionDetailsOpts } from '../../context'; +import { useConnectionDetailsOpts, useConnectionDetailsService } from '../../context'; +import { useBehaviorSubject } from '../../hooks/use_behavior_subject'; import { CloudIdRow } from './rows/cloud_id_row'; import { EndpointUrlRow } from './rows/endpoints_url_row'; export const EndpointsTab: React.FC = () => { const { endpoints } = useConnectionDetailsOpts(); + const service = useConnectionDetailsService(); + const showCloudId = useBehaviorSubject(service.showCloudId$); if (!endpoints) return null; return ( - {!!endpoints?.url && } - {!!endpoints?.id && } + {!!endpoints?.url && ( + service.emitTelemetryEvent(['copy_endpoint_url_clicked'])} + /> + )} + {!!endpoints?.id && ( + service.emitTelemetryEvent(['copy_cloud_id_clicked'])} + /> + )} ); }; diff --git a/packages/cloud/connection_details/tabs/endpoints_tab/rows/cloud_id_row/cloud_id_row.tsx b/packages/cloud/connection_details/tabs/endpoints_tab/rows/cloud_id_row/cloud_id_row.tsx index ddaddb8d6ce5d..53d31baaef8f1 100644 --- a/packages/cloud/connection_details/tabs/endpoints_tab/rows/cloud_id_row/cloud_id_row.tsx +++ b/packages/cloud/connection_details/tabs/endpoints_tab/rows/cloud_id_row/cloud_id_row.tsx @@ -10,18 +10,23 @@ import * as React from 'react'; import { EuiFormRow, EuiSpacer, EuiSwitch } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { CopyInput } from '../../../../components/copy_input'; -import { useConnectionDetailsService } from '../../../../context'; -import { useBehaviorSubject } from '../../../../hooks/use_behavior_subject'; import { Label } from './label'; export interface CloudIdRowProps { value: string; + showCloudId: boolean; + learnMoreUrl?: string; + onShowCloudIdToggle: () => void; + onCopyClick?: () => void; } -export const CloudIdRow: React.FC = ({ value }) => { - const service = useConnectionDetailsService(); - const showCloudId = useBehaviorSubject(service.showCloudId$); - +export const CloudIdRow: React.FC = ({ + value, + showCloudId, + learnMoreUrl, + onShowCloudIdToggle, + onCopyClick, +}) => { return ( <> @@ -31,7 +36,7 @@ export const CloudIdRow: React.FC = ({ value }) => { defaultMessage: 'Show Cloud ID', })} checked={showCloudId} - onChange={service.toggleShowCloudId} + onChange={() => onShowCloudIdToggle()} data-test-subj="connectionDetailsCloudIdSwitch" /> @@ -39,7 +44,7 @@ export const CloudIdRow: React.FC = ({ value }) => { {showCloudId && ( } + label={ )} diff --git a/packages/cloud/connection_details/tabs/endpoints_tab/rows/endpoints_url_row.tsx b/packages/cloud/connection_details/tabs/endpoints_tab/rows/endpoints_url_row.tsx index 29f40dcb0c5e2..7eb9683b11ed6 100644 --- a/packages/cloud/connection_details/tabs/endpoints_tab/rows/endpoints_url_row.tsx +++ b/packages/cloud/connection_details/tabs/endpoints_tab/rows/endpoints_url_row.tsx @@ -13,9 +13,10 @@ import { CopyInput } from '../../../components/copy_input'; export interface EndpointUrlProps { url: string; + onCopyClick?: () => void; } -export const EndpointUrlRow: React.FC = ({ url }) => { +export const EndpointUrlRow: React.FC = ({ url, onCopyClick }) => { return ( = ({ url }) => { fullWidth data-test-subj="connectionDetailsEsUrl" > - + onCopyClick?.()} /> ); }; diff --git a/packages/cloud/connection_details/types.ts b/packages/cloud/connection_details/types.ts index 81b51a444ed78..593f2b57ee3ac 100644 --- a/packages/cloud/connection_details/types.ts +++ b/packages/cloud/connection_details/types.ts @@ -13,6 +13,7 @@ export interface ConnectionDetailsOpts { endpoints?: ConnectionDetailsOptsEndpoints; apiKeys?: ConnectionDetailsOptsApiKeys; navigateToUrl?: (url: string) => void; + onTelemetryEvent?: (event: ConnectionDetailsTelemetryEvents) => void; } export interface ConnectionDetailsOptsLinks { @@ -33,4 +34,20 @@ export interface ConnectionDetailsOptsApiKeys { hasPermission: () => Promise; } +export type ConnectionDetailsTelemetryEvent = [ + id: EventId, + payload?: EventPayload +]; + +export type ConnectionDetailsTelemetryEvents = + | ConnectionDetailsTelemetryEvent<'learn_more_clicked'> + | ConnectionDetailsTelemetryEvent<'tab_switched', { tab: string }> + | ConnectionDetailsTelemetryEvent<'copy_endpoint_url_clicked'> + | ConnectionDetailsTelemetryEvent<'show_cloud_id_toggled'> + | ConnectionDetailsTelemetryEvent<'copy_cloud_id_clicked'> + | ConnectionDetailsTelemetryEvent<'new_api_key_created'> + | ConnectionDetailsTelemetryEvent<'manage_api_keys_clicked'> + | ConnectionDetailsTelemetryEvent<'key_encoding_changed', { format: string }> + | ConnectionDetailsTelemetryEvent<'copy_api_key_clicked', { format: string }>; + export type TabID = 'endpoints' | 'apiKeys'; diff --git a/packages/core/http/core-http-server-internal/src/http_server.test.ts b/packages/core/http/core-http-server-internal/src/http_server.test.ts index 129efea82cc8c..235fab185e12d 100644 --- a/packages/core/http/core-http-server-internal/src/http_server.test.ts +++ b/packages/core/http/core-http-server-internal/src/http_server.test.ts @@ -1622,7 +1622,7 @@ describe('setup contract', () => { .get('/static/some_json.json') .expect(200); - const etag = response.get('etag'); + const etag = response.get('etag')!; expect(etag).not.toBeUndefined(); await supertest(innerServer.listener) diff --git a/packages/core/i18n/core-i18n-browser-internal/src/__snapshots__/i18n_service.test.tsx.snap b/packages/core/i18n/core-i18n-browser-internal/src/__snapshots__/i18n_service.test.tsx.snap index 29a42c3a4c654..fd8ba14e035cf 100644 --- a/packages/core/i18n/core-i18n-browser-internal/src/__snapshots__/i18n_service.test.tsx.snap +++ b/packages/core/i18n/core-i18n-browser-internal/src/__snapshots__/i18n_service.test.tsx.snap @@ -6,8 +6,8 @@ exports[`#start() returns \`Context\` component 1`] = ` i18n={ Object { "mapping": Object { + "euiAbsoluteTab.dateFormatButtonLabel": "Parse date", "euiAbsoluteTab.dateFormatError": [Function], - "euiAbsoluteTab.dateFormatHint": "Press the Enter key to parse as a date.", "euiAccordionChildrenLoading.message": "Loading", "euiAutoRefresh.autoRefreshLabel": "Auto refresh", "euiAutoRefresh.buttonLabelOff": "Auto refresh is off", @@ -36,6 +36,7 @@ exports[`#start() returns \`Context\` component 1`] = ` "euiCodeBlockFullScreen.fullscreenExpand": "Expand", "euiCollapsedItemActions.allActions": "All actions", "euiCollapsedItemActions.allActionsDisabled": "Individual item actions are disabled when rows are being selected.", + "euiCollapsedNavButton.ariaLabelButtonIcon": [Function], "euiCollapsibleNavBeta.ariaLabel": "Site menu", "euiCollapsibleNavButton.ariaLabelClose": "Close navigation", "euiCollapsibleNavButton.ariaLabelCollapse": "Collapse navigation", diff --git a/packages/core/i18n/core-i18n-browser-internal/src/i18n_eui_mapping.tsx b/packages/core/i18n/core-i18n-browser-internal/src/i18n_eui_mapping.tsx index c7d290f3af603..386e7a55da83b 100644 --- a/packages/core/i18n/core-i18n-browser-internal/src/i18n_eui_mapping.tsx +++ b/packages/core/i18n/core-i18n-browser-internal/src/i18n_eui_mapping.tsx @@ -179,6 +179,11 @@ export const getEuiContextMapping = (): EuiTokensObject => { 'euiCollapsibleNavBeta.ariaLabel': i18n.translate('core.euiCollapsibleNavBeta.ariaLabel', { defaultMessage: 'Site menu', }), + 'euiCollapsedNavButton.ariaLabelButtonIcon': ({ title }: EuiValues) => + i18n.translate('core.euiCollapsedNavButton.ariaLabelButtonIcon', { + defaultMessage: '{title}, quick navigation menu', + values: { title }, + }), 'euiCollapsibleNavButton.ariaLabelExpand': i18n.translate( 'core.euiCollapsibleNavButton.ariaLabelExpand', { defaultMessage: 'Expand navigation' } @@ -1413,9 +1418,12 @@ export const getEuiContextMapping = (): EuiTokensObject => { 'euiRelativeTab.dateInputError': i18n.translate('core.euiRelativeTab.dateInputError', { defaultMessage: 'Must be a valid range', }), - 'euiAbsoluteTab.dateFormatHint': i18n.translate('core.euiAbsoluteTab.dateFormatHint', { - defaultMessage: 'Press the Enter key to parse as a date.', - }), + 'euiAbsoluteTab.dateFormatButtonLabel': i18n.translate( + 'core.euiAbsoluteTab.dateFormatButtonLabel', + { + defaultMessage: 'Parse date', + } + ), 'euiResizableButton.horizontalResizerAriaLabel': i18n.translate( 'core.euiResizableButton.horizontalResizerAriaLabel', { diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/constants.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/constants.ts index a04e762eaf67d..4244a5fed91aa 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/constants.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/constants.ts @@ -157,6 +157,7 @@ export const HASH_TO_VERSION_MAP = { 'core-usage-stats|3d1b76c39bfb2cc8296b024d73854724': '7.14.1', 'csp-rule-template|6ee70dc06c0ca3ddffc18222f202ab25': '10.0.0', 'dashboard|b8aa800aa5e0d975c5e8dc57f03d41f8': '10.2.0', + 'endpoint:unified-user-artifact-manifest|393c6e4f5f16288c24ef9057e4d76a4c': '10.0.0', 'endpoint:user-artifact-manifest|7502b5c5bc923abe8aa5ccfd636e8c3d': '10.0.0', 'enterprise_search_telemetry|3d1b76c39bfb2cc8296b024d73854724': '10.0.0', 'epm-packages-assets|44621b2f6052ef966da47b7c3a00f33b': '10.0.0', diff --git a/packages/kbn-check-mappings-update-cli/current_fields.json b/packages/kbn-check-mappings-update-cli/current_fields.json index 65fd6af2ce919..04343968ef958 100644 --- a/packages/kbn-check-mappings-update-cli/current_fields.json +++ b/packages/kbn-check-mappings-update-cli/current_fields.json @@ -280,6 +280,11 @@ "title", "version" ], + "endpoint:unified-user-artifact-manifest": [ + "artifactIds", + "policyId", + "semanticVersion" + ], "endpoint:user-artifact-manifest": [ "artifacts", "schemaVersion" diff --git a/packages/kbn-check-mappings-update-cli/current_mappings.json b/packages/kbn-check-mappings-update-cli/current_mappings.json index 03a189b1549be..6ee1f6d13521f 100644 --- a/packages/kbn-check-mappings-update-cli/current_mappings.json +++ b/packages/kbn-check-mappings-update-cli/current_mappings.json @@ -964,6 +964,20 @@ } } }, + "endpoint:unified-user-artifact-manifest": { + "dynamic": false, + "properties": { + "artifactIds": { + "type": "keyword" + }, + "policyId": { + "type": "keyword" + }, + "semanticVersion": { + "type": "keyword" + } + } + }, "endpoint:user-artifact-manifest": { "dynamic": false, "properties": { diff --git a/packages/kbn-cli-dev-mode/src/integration_tests/base_path_proxy_server.test.ts b/packages/kbn-cli-dev-mode/src/integration_tests/base_path_proxy_server.test.ts index 5e8a7a50a7b32..0f0a69638cfa2 100644 --- a/packages/kbn-cli-dev-mode/src/integration_tests/base_path_proxy_server.test.ts +++ b/packages/kbn-cli-dev-mode/src/integration_tests/base_path_proxy_server.test.ts @@ -28,7 +28,7 @@ describe('BasePathProxyServer', () => { let logger: TestLog; let config: IHttpConfig; let basePath: string; - let proxySupertest: supertest.SuperTest; + let proxySupertest: supertest.Agent; beforeEach(async () => { logger = new TestLog(); @@ -330,7 +330,7 @@ describe('BasePathProxyServer', () => { describe('shouldRedirect', () => { let proxyServerWithoutShouldRedirect: BasePathProxyServer; - let proxyWithoutShouldRedirectSupertest: supertest.SuperTest; + let proxyWithoutShouldRedirectSupertest: supertest.Agent; beforeEach(async () => { // setup and start a proxy server which does not use "shouldRedirectFromOldBasePath" @@ -373,7 +373,7 @@ describe('BasePathProxyServer', () => { describe('constructor option for sending in a custom basePath', () => { let proxyServerWithFooBasePath: BasePathProxyServer; - let proxyWithFooBasePath: supertest.SuperTest; + let proxyWithFooBasePath: supertest.Agent; beforeEach(async () => { // setup and start a proxy server which uses a basePath of "foo" diff --git a/packages/kbn-content-management-utils/src/saved_object_content_storage.ts b/packages/kbn-content-management-utils/src/saved_object_content_storage.ts index d462c7a02b8de..36d9979cfdc8a 100644 --- a/packages/kbn-content-management-utils/src/saved_object_content_storage.ts +++ b/packages/kbn-content-management-utils/src/saved_object_content_storage.ts @@ -375,8 +375,8 @@ export abstract class SOContentStorage } const { value: optionsToLatest, error: optionsError } = transforms.update.in.options.up< - Types['CreateOptions'], - Types['CreateOptions'] + Types['UpdateOptions'], + Types['UpdateOptions'] >(options); if (optionsError) { throw Boom.badRequest(`Invalid options. ${optionsError.message}`); diff --git a/packages/kbn-content-management-utils/src/schema.ts b/packages/kbn-content-management-utils/src/schema.ts index 2e95624c36a22..5b07a464552c8 100644 --- a/packages/kbn-content-management-utils/src/schema.ts +++ b/packages/kbn-content-management-utils/src/schema.ts @@ -111,6 +111,7 @@ export const updateOptionsSchema = { refresh: schema.maybe(schema.oneOf([schema.boolean(), schema.literal('wait_for')])), upsert: (attributesSchema: ObjectType) => schema.maybe(savedObjectSchema(attributesSchema)), retryOnConflict: schema.maybe(schema.number()), + mergeAttributes: schema.maybe(schema.boolean()), }; export const createResultSchema = (soSchema: ObjectType) => diff --git a/packages/kbn-content-management-utils/src/types.ts b/packages/kbn-content-management-utils/src/types.ts index 72f2093acf2ad..6417d9af1e888 100644 --- a/packages/kbn-content-management-utils/src/types.ts +++ b/packages/kbn-content-management-utils/src/types.ts @@ -173,6 +173,13 @@ export interface SavedObjectUpdateOptions { * Defaults to `0` when `version` is provided, `3` otherwise. */ retryOnConflict?: number; + /** + * By default, update will merge the provided attributes with the ones present on the document + * (performing a standard partial update). Setting this option to `false` will change the behavior, performing + * a "full" update instead, where the provided attributes will fully replace the existing ones. + * Defaults to `true`. + */ + mergeAttributes?: boolean; } /** Return value for Saved Object get, T is item returned */ diff --git a/packages/kbn-discover-utils/src/utils/is_legacy_table_enabled.ts b/packages/kbn-discover-utils/src/utils/is_legacy_table_enabled.ts index 7e575cf80dbfb..8aabd92201664 100644 --- a/packages/kbn-discover-utils/src/utils/is_legacy_table_enabled.ts +++ b/packages/kbn-discover-utils/src/utils/is_legacy_table_enabled.ts @@ -11,12 +11,12 @@ import { DOC_TABLE_LEGACY } from '../constants'; export function isLegacyTableEnabled({ uiSettings, - isTextBasedQueryMode, + isEsqlMode, }: { uiSettings: IUiSettingsClient; - isTextBasedQueryMode: boolean; + isEsqlMode: boolean; }): boolean { - if (isTextBasedQueryMode) { + if (isEsqlMode) { return false; // only show the new data grid } diff --git a/packages/kbn-doc-links/src/get_doc_links.ts b/packages/kbn-doc-links/src/get_doc_links.ts index 136e5f7bc95b1..68bd793b3d5c9 100644 --- a/packages/kbn-doc-links/src/get_doc_links.ts +++ b/packages/kbn-doc-links/src/get_doc_links.ts @@ -479,6 +479,7 @@ export const getDocLinks = ({ kibanaBranch, buildFlavor }: GetDocLinkOptions): D }, privileges: `${SECURITY_SOLUTION_DOCS}endpoint-management-req.html`, manageDetectionRules: `${SECURITY_SOLUTION_DOCS}rules-ui-management.html`, + createDetectionRules: `${SECURITY_SOLUTION_DOCS}rules-ui-create.html`, createEsqlRuleType: `${SECURITY_SOLUTION_DOCS}rules-ui-create.html#create-esql-rule`, ruleUiAdvancedParams: `${SECURITY_SOLUTION_DOCS}rules-ui-create.html#rule-ui-advanced-params`, entityAnalytics: { diff --git a/packages/kbn-doc-links/src/types.ts b/packages/kbn-doc-links/src/types.ts index 29e09d8b25672..fb59c867cff9d 100644 --- a/packages/kbn-doc-links/src/types.ts +++ b/packages/kbn-doc-links/src/types.ts @@ -354,6 +354,7 @@ export interface DocLinks { }; readonly privileges: string; readonly manageDetectionRules: string; + readonly createDetectionRules: string; readonly createEsqlRuleType: string; readonly ruleUiAdvancedParams: string; readonly entityAnalytics: { diff --git a/packages/kbn-esql-utils/src/utils/append_to_query.test.ts b/packages/kbn-esql-utils/src/utils/append_to_query.test.ts index cb9465cff05b6..2f3d28c467444 100644 --- a/packages/kbn-esql-utils/src/utils/append_to_query.test.ts +++ b/packages/kbn-esql-utils/src/utils/append_to_query.test.ts @@ -65,7 +65,7 @@ describe('appendToQuery', () => { 'from logstash-* // meow', 'dest', undefined, - '_exists_', + 'is_not_null', 'string' ) ).toBe( @@ -74,6 +74,21 @@ describe('appendToQuery', () => { ); }); + it('appends a where clause in an existing query checking that the value is null if the user filters a null value', () => { + expect( + appendWhereClauseToESQLQuery( + 'from logstash-* // meow', + 'dest', + undefined, + 'is_null', + 'string' + ) + ).toBe( + `from logstash-* // meow +| where \`dest\` is null` + ); + }); + it('appends an and clause in an existing query with where command as the last pipe', () => { expect( appendWhereClauseToESQLQuery( @@ -107,7 +122,7 @@ and \`dest\`=="Crete"` 'from logstash-* | where country IS NOT NULL', 'country', undefined, - '_exists_', + 'is_not_null', 'string' ) ).toBe(`from logstash-* | where country IS NOT NULL`); diff --git a/packages/kbn-esql-utils/src/utils/append_to_query.ts b/packages/kbn-esql-utils/src/utils/append_to_query.ts index d8f6ed3a44073..d1bf0afa33755 100644 --- a/packages/kbn-esql-utils/src/utils/append_to_query.ts +++ b/packages/kbn-esql-utils/src/utils/append_to_query.ts @@ -17,14 +17,17 @@ export function appendWhereClauseToESQLQuery( baseESQLQuery: string, field: string, value: unknown, - operation: '+' | '-' | '_exists_', + operation: '+' | '-' | 'is_not_null' | 'is_null', fieldType?: string ): string { let operator; switch (operation) { - case '_exists_': + case 'is_not_null': operator = ' is not null'; break; + case 'is_null': + operator = ' is null'; + break; case '-': operator = '!='; break; @@ -44,7 +47,7 @@ export function appendWhereClauseToESQLQuery( // checking that the value is not null // this is the existence filter - if (operation === '_exists_') { + if (operation === 'is_not_null' || operation === 'is_null') { fieldName = `\`${String(field)}\``; filterValue = ''; } @@ -67,7 +70,7 @@ export function appendWhereClauseToESQLQuery( const matches = whereClause.match(new RegExp(field + '(.*)' + String(filterValue))); if (matches) { const existingOperator = matches[1]?.trim().replace('`', '').toLowerCase(); - if (!['==', '!=', 'is not null'].includes(existingOperator.trim())) { + if (!['==', '!=', 'is not null', 'is null'].includes(existingOperator.trim())) { return appendToESQLQuery(baseESQLQuery, `and ${fieldName}${operator}${filterValue}`); } // the filter is the same diff --git a/packages/kbn-search-api-panels/components/ingest_data.tsx b/packages/kbn-search-api-panels/components/ingest_data.tsx index 0700d2d56d661..e842c499630bb 100644 --- a/packages/kbn-search-api-panels/components/ingest_data.tsx +++ b/packages/kbn-search-api-panels/components/ingest_data.tsx @@ -13,6 +13,8 @@ import { i18n } from '@kbn/i18n'; import type { ApplicationStart } from '@kbn/core-application-browser'; import type { ConsolePluginStart } from '@kbn/console-plugin/public'; import type { SharePluginStart } from '@kbn/share-plugin/public'; +import { IngestGetPipelineResponse } from '@elastic/elasticsearch/lib/api/types'; +import { IngestPipelinePanel } from './ingest_pipelines/ingest_pipeline_panel'; import { CodeBox } from './code_box'; import { LanguageDefinition } from '../types'; import { OverviewPanel } from './overview_panel'; @@ -32,11 +34,16 @@ interface IngestDataProps { languages: LanguageDefinition[]; consoleRequest?: string; additionalIngestionPanel?: React.ReactNode; + ingestPipelineData?: IngestGetPipelineResponse; + selectedPipeline: string; + setSelectedPipeline: (pipelineId: string) => void; + defaultIngestPipeline: string; } export const IngestData: React.FC = ({ codeSnippet, selectedLanguage, + selectedPipeline, setSelectedLanguage, docLinks, assetBasePath, @@ -46,6 +53,9 @@ export const IngestData: React.FC = ({ languages, consoleRequest, additionalIngestionPanel, + ingestPipelineData, + setSelectedPipeline, + defaultIngestPipeline, }) => { return ( = ({ })} > +

{i18n.translate('searchApiPanels.welcomeBanner.ingestData.alternativeOptions', { diff --git a/packages/kbn-search-api-panels/components/ingest_pipelines/ingest_pipeline_options.tsx b/packages/kbn-search-api-panels/components/ingest_pipelines/ingest_pipeline_options.tsx new file mode 100644 index 0000000000000..6a4888a643d4f --- /dev/null +++ b/packages/kbn-search-api-panels/components/ingest_pipelines/ingest_pipeline_options.tsx @@ -0,0 +1,126 @@ +/* + * Copyright 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 { EuiFlexItem, EuiText, EuiBadge, EuiFlexGroup } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { IngestPipeline } from '@elastic/elasticsearch/lib/api/types'; +import { IngestGetPipelineResponse } from '@elastic/elasticsearch/lib/api/types'; + +interface OptionItem { + value: string; + inputDisplay: string; + dropdownDisplay: JSX.Element; +} + +export interface IngestPipelineWithDeprecated extends IngestPipeline { + deprecated?: boolean; +} + +const ProcessorCount = ({ item }: { item: IngestPipelineWithDeprecated | undefined }) => ( + + +

+ {i18n.translate('searchApiPanels.welcomeBanner.ingestPipelinePanel.processorCount', { + defaultMessage: '{count} {count, plural, one {processor} other {processors}}', + values: { count: item?.processors?.length }, + })} +

+
+
+); + +const ManagedBadge = ({ item }: { item: IngestPipelineWithDeprecated | undefined }) => { + if (!item?._meta?.managed) return null; + return ( + + + {i18n.translate('searchApiPanels.welcomeBanner.ingestPipelinePanel.managedBadge', { + defaultMessage: 'Managed', + })} + + + ); +}; + +const RecommendedBadge = ({ + pipelineName, + defaultIngestPipeline, +}: { + pipelineName: string; + defaultIngestPipeline: string; +}) => { + if (pipelineName !== defaultIngestPipeline) return null; + return ( + + + {i18n.translate('searchApiPanels.welcomeBanner.ingestPipelinePanel.recommendedBadge', { + defaultMessage: 'Recommended', + })} + + + ); +}; + +const createOptionItem = ( + pipelineName: string, + item: IngestPipelineWithDeprecated | undefined, + defaultIngestPipeline: string +): OptionItem => { + return { + value: pipelineName, + inputDisplay: pipelineName, + dropdownDisplay: ( + + {pipelineName} + + + + + + + ), + }; +}; + +export const createIngestPipelineOptions = ( + ingestPipelinesData: IngestGetPipelineResponse | undefined, + defaultIngestPipeline: string +) => { + if (!ingestPipelinesData) return []; + + let options = Object.keys(ingestPipelinesData) + .filter( + (pipelineName: string) => + !(ingestPipelinesData[pipelineName] as IngestPipelineWithDeprecated)?.deprecated + ) + .map((pipelineName) => + createOptionItem(pipelineName, ingestPipelinesData[pipelineName], defaultIngestPipeline) + ); + + if (ingestPipelinesData[defaultIngestPipeline]) { + const defaultOption = createOptionItem( + defaultIngestPipeline, + ingestPipelinesData[defaultIngestPipeline], + defaultIngestPipeline + ); + options = [ + defaultOption, + ...options.filter((option) => option.value !== defaultIngestPipeline), + ]; + } + + return options; +}; diff --git a/packages/kbn-search-api-panels/components/ingest_pipelines/ingest_pipeline_panel.test.tsx b/packages/kbn-search-api-panels/components/ingest_pipelines/ingest_pipeline_panel.test.tsx new file mode 100644 index 0000000000000..040136864f295 --- /dev/null +++ b/packages/kbn-search-api-panels/components/ingest_pipelines/ingest_pipeline_panel.test.tsx @@ -0,0 +1,118 @@ +/* + * Copyright 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 { registerTestBed } from '@kbn/test-jest-helpers'; +import { act } from 'react-dom/test-utils'; +import { IngestPipelinePanel } from './ingest_pipeline_panel'; + +const DEFAULT_INGESTION_PIPELINE = 'default-ingestion-pipeline'; + +describe('IngestPipelinePanel', () => { + const setSelectedPipelineMock = jest.fn(); + + const mockPipelineData = { + pipeline1: { + processors: ['processor1', 'processor2'], + _meta: { + managed: true, + }, + }, + pipeline2: { + processors: ['processor1'], + _meta: { + managed: false, + }, + }, + [DEFAULT_INGESTION_PIPELINE]: { + processors: ['processor1', 'processor2', 'processor3'], + _meta: { + managed: true, + }, + }, + deprecated_pipeline: { + processors: ['processor1'], + _meta: { + managed: false, + }, + deprecated: true, + }, + } as any; + + let exists: any; + let find: any; + + beforeAll(async () => { + const setup = registerTestBed(IngestPipelinePanel, { + defaultProps: { + setSelectedPipeline: setSelectedPipelineMock, + ingestPipelinesData: mockPipelineData, + defaultIngestPipeline: DEFAULT_INGESTION_PIPELINE, + }, + memoryRouter: { wrapComponent: false }, + }); + + await act(async () => { + const testBed = setup(); + exists = testBed.exists; + find = testBed.find; + }); + }); + + it('should display Process Data section', () => { + expect(exists('ingestPipelinePanelTitle')).toBe(true); + expect(find('ingestPipelinePanelTitle').contains('Preprocess your data')).toBe(true); + expect( + find('ingestPipelinePanelBody').contains( + 'You can use ingest pipelines to preprocess data before indexing into Elasticsearch.' + ) + ).toBe(true); + expect(find('ingestPipelinePanelTitle').find('.euiBadge__text').contains('Optional')).toBe( + true + ); + }); + + it('should display number of processors', () => { + find('ingestPipelinePanelSelect').simulate('click'); + expect(find('ingestPipelinePanelOptions').at(0).contains('3 processors')).toBe(true); + expect(find('ingestPipelinePanelOptions').at(1).contains('2 processors')).toBe(true); + expect(find('ingestPipelinePanelOptions').at(2).contains('1 processor')).toBe(true); + }); + + it('should display the badges correctly', () => { + find('ingestPipelinePanelSelect').simulate('click'); + expect( + find('ingestPipelinePanelOptions').at(0).find('.euiBadge__text').contains('Recommended') + ).toBe(true); + expect( + find('ingestPipelinePanelOptions').at(1).find('.euiBadge__text').contains('Managed') + ).toBe(true); + expect( + find('ingestPipelinePanelOptions').at(2).find('.euiBadge__text').contains('Managed') + ).toBe(false); + }); + + it('should display only active pipelines', () => { + find('ingestPipelinePanelSelect').simulate('click'); + expect(find('ingestPipelinePanelOptionTitle').contains('pipeline1')).toBe(true); + expect(find('ingestPipelinePanelOptionTitle').contains('deprecated_pipeline')).toBe(false); + }); + + it('should display the recommended pipeline at the beginning', () => { + find('ingestPipelinePanelSelect').simulate('click'); + expect(find('ingestPipelinePanelOptionTitle').at(0).contains(DEFAULT_INGESTION_PIPELINE)).toBe( + true + ); + }); + + describe('when there exists no ingest pipeline', () => { + it('should display an empty list of pipelines', () => { + find('ingestPipelinePanelSelect').simulate('click'); + expect(exists('ingestPipelinePanelOptions')).toBe(false); + }); + }); +}); diff --git a/packages/kbn-search-api-panels/components/ingest_pipelines/ingest_pipeline_panel.tsx b/packages/kbn-search-api-panels/components/ingest_pipelines/ingest_pipeline_panel.tsx new file mode 100644 index 0000000000000..a746b2bc0e61e --- /dev/null +++ b/packages/kbn-search-api-panels/components/ingest_pipelines/ingest_pipeline_panel.tsx @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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, { useMemo } from 'react'; + +import { + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiText, + EuiTitle, + EuiBadge, + EuiSuperSelect, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { IngestGetPipelineResponse } from '@elastic/elasticsearch/lib/api/types'; +import { createIngestPipelineOptions } from './ingest_pipeline_options'; + +interface IngestPipelinePanelProps { + selectedPipeline: string; + setSelectedPipeline: (pipeline: string) => void; + ingestPipelinesData?: IngestGetPipelineResponse; + defaultIngestPipeline: string; +} + +export const IngestPipelinePanel: React.FC = ({ + selectedPipeline, + setSelectedPipeline, + ingestPipelinesData, + defaultIngestPipeline, +}) => { + const options = useMemo( + () => createIngestPipelineOptions(ingestPipelinesData, defaultIngestPipeline), + [ingestPipelinesData, defaultIngestPipeline] + ); + + return ( + <> + + + + + {i18n.translate('searchApiPanels.welcomeBanner.ingestPipelinePanel.title', { + defaultMessage: 'Preprocess your data', + })} + + + + + + {i18n.translate('searchApiPanels.welcomeBanner.ingestPipelinePanel.optionalBadge', { + defaultMessage: 'Optional', + })} + + + + + +

+ {i18n.translate('searchApiPanels.welcomeBanner.ingestPipelinePanel.description', { + defaultMessage: + 'You can use ingest pipelines to preprocess data before indexing into Elasticsearch.', + })} +

+
+ + + + + ); +}; diff --git a/packages/kbn-search-api-panels/index.tsx b/packages/kbn-search-api-panels/index.tsx index 10276bc9c5325..f0a64685348de 100644 --- a/packages/kbn-search-api-panels/index.tsx +++ b/packages/kbn-search-api-panels/index.tsx @@ -13,6 +13,7 @@ import { AuthenticatedUser } from '@kbn/security-plugin/common'; export * from './components/cloud_details'; export * from './components/code_box'; +export * from './components/ingest_pipelines/ingest_pipeline_panel'; export * from './components/github_link'; export * from './components/ingest_data'; export * from './components/ingestions_panel'; diff --git a/packages/kbn-search-api-panels/languages/console.ts b/packages/kbn-search-api-panels/languages/console.ts index e156409239242..27aeb96b93ba4 100644 --- a/packages/kbn-search-api-panels/languages/console.ts +++ b/packages/kbn-search-api-panels/languages/console.ts @@ -19,7 +19,9 @@ export const consoleDefinition: Partial = { } } }`, - ingestData: `POST _bulk?pretty + ingestData: ({ ingestPipeline }) => `POST _bulk?pretty${ + ingestPipeline ? `&pipeline=${ingestPipeline}` : '' + } { "index" : { "_index" : "books" } } {"name": "Snow Crash", "author": "Neal Stephenson", "release_date": "1992-06-01", "page_count": 470} { "index" : { "_index" : "books" } } diff --git a/packages/kbn-search-api-panels/tsconfig.json b/packages/kbn-search-api-panels/tsconfig.json index 20294566e4cc3..a3b9c16a04512 100644 --- a/packages/kbn-search-api-panels/tsconfig.json +++ b/packages/kbn-search-api-panels/tsconfig.json @@ -23,6 +23,7 @@ "@kbn/security-plugin", "@kbn/console-plugin", "@kbn/ui-theme", - "@kbn/try-in-console" + "@kbn/try-in-console", + "@kbn/test-jest-helpers" ] } diff --git a/packages/kbn-search-api-panels/utils.test.ts b/packages/kbn-search-api-panels/utils.test.ts index c842dd03cf275..ef77838250312 100644 --- a/packages/kbn-search-api-panels/utils.test.ts +++ b/packages/kbn-search-api-panels/utils.test.ts @@ -12,8 +12,8 @@ import { getConsoleRequest } from './utils'; describe('utils', () => { describe('getConsoleRequest()', () => { test('accepts string values', () => { - const consoleRequest = getConsoleRequest('ingestData'); - expect(consoleRequest).toEqual(consoleDefinition.ingestData); + const consoleRequest = getConsoleRequest('buildSearchQuery'); + expect(consoleRequest).toEqual(consoleDefinition.buildSearchQuery); }); test('accepts function values', () => { diff --git a/packages/kbn-securitysolution-utils/src/esql/get_index_list_from_esql_query.test.ts b/packages/kbn-securitysolution-utils/src/esql/get_index_list_from_esql_query.test.ts index ece5c72d5d5ff..5965e51e694e3 100644 --- a/packages/kbn-securitysolution-utils/src/esql/get_index_list_from_esql_query.test.ts +++ b/packages/kbn-securitysolution-utils/src/esql/get_index_list_from_esql_query.test.ts @@ -33,4 +33,9 @@ describe('getIndexListFromEsqlQuery', () => { getIndexPatternFromESQLQueryMock.mockReturnValue('test-1 , test-2 '); expect(getIndexListFromEsqlQuery('From test-1, test-2 ')).toEqual(['test-1', 'test-2']); }); + + it('should return empty array when getIndexPatternFromESQLQuery throws error', () => { + getIndexPatternFromESQLQueryMock.mockReturnValue(new Error('Fail to parse')); + expect(getIndexListFromEsqlQuery('From test-1 []')).toEqual([]); + }); }); diff --git a/packages/kbn-securitysolution-utils/src/esql/get_index_list_from_esql_query.ts b/packages/kbn-securitysolution-utils/src/esql/get_index_list_from_esql_query.ts index 64374732dc716..9464f041bdd64 100644 --- a/packages/kbn-securitysolution-utils/src/esql/get_index_list_from_esql_query.ts +++ b/packages/kbn-securitysolution-utils/src/esql/get_index_list_from_esql_query.ts @@ -11,9 +11,13 @@ import { getIndexPatternFromESQLQuery } from '@kbn/esql-utils'; * parses ES|QL query and returns array of indices */ export const getIndexListFromEsqlQuery = (query: string | undefined): string[] => { - const indexString = getIndexPatternFromESQLQuery(query); + try { + const indexString = getIndexPatternFromESQLQuery(query); - return getIndexListFromIndexString(indexString); + return getIndexListFromIndexString(indexString); + } catch (e) { + return []; + } }; /** diff --git a/packages/kbn-std/src/is_internal_url.test.ts b/packages/kbn-std/src/is_internal_url.test.ts index cc1f5a32d7db4..24e043a72073c 100644 --- a/packages/kbn-std/src/is_internal_url.test.ts +++ b/packages/kbn-std/src/is_internal_url.test.ts @@ -61,6 +61,84 @@ describe('isInternalURL', () => { const href = `${basePath}/app/kibana/../../../management`; expect(isInternalURL(href, basePath)).toBe(false); }); + + it('should return `false` if absolute URL contains tabs or new lines', () => { + // These URLs can either be treated as internal or external depending on the presence of the special characters. + const getURLsWithCharInRelativeScheme = (char: string) => [ + `/${char}${basePath}app/kibana`, + `/${char}/${basePath}/app/kibana`, + `/${char}${basePath}/example.com`, + `/${char}/${basePath}/example.com`, + `/${char}${basePath}/example.org`, + `/${char}/${basePath}/example.org`, + `/${char}${basePath}/example.org:5601`, + `/${char}/${basePath}/example.org:5601`, + ]; + + // These URLs can either be treated as internal or external depending on the presence of the special characters + // AND spaces since these affect how URL's scheme is parsed. + const getURLsWithCharInScheme = (char: string) => [ + `htt${char}ps://example.com${basePath}`, + `htt${char}ps://example.org${basePath}`, + `htt${char}ps://example.org:5601${basePath}`, + `java${char}script:${basePath}alert(1)`, + `htt${char}p:/${basePath}/example.org:5601`, + `file${char}:/${basePath}/example.com`, + ]; + + // These URLs should always be recognized as external irrespective to the presence of the special characters or + // spaces since these affect URLs hostname. + const getURLsWithCharInHost = (char: string) => [ + `//${char}${basePath}/app/kibana`, + `//${char}/example.com${basePath}`, + `//${char}/example.org${basePath}`, + `//${char}/example.org:5601${basePath}`, + `https:/${char}/example.com${basePath}`, + // This URL is only valid if the `char` is a tab or newline character. + `https://example${char}.com${basePath}/path`, + ]; + + // Detection logic should treat URLs as if tab and newline characters weren't present. + for (const char of ['', '\t', '\n', '\r', '\t\n\r']) { + for (const url of [ + ...getURLsWithCharInRelativeScheme(char), + ...getURLsWithCharInScheme(char), + ...getURLsWithCharInHost(char), + ]) { + expect(isInternalURL(url)).toBe(false); + } + } + + // Characters that aren't tabs, spaces, or newline characters turn absolute scheme-relative URLs to relative URLs. + for (const char of ['1', 'a']) { + for (const url of getURLsWithCharInRelativeScheme(char)) { + expect(isInternalURL(url)).toBe(true); + } + + for (const url of getURLsWithCharInScheme(char)) { + expect(isInternalURL(url)).toBe(false); + } + + for (const url of getURLsWithCharInHost(char)) { + expect(isInternalURL(url)).toBe(false); + } + } + + // Spaces aren't allowed in scheme definition and turn absolute URLs into relative URLs. + for (const char of [' ']) { + for (const url of getURLsWithCharInRelativeScheme(char)) { + expect(isInternalURL(url)).toBe(true); + } + + for (const url of getURLsWithCharInScheme(char)) { + expect(isInternalURL(url)).toBe(true); + } + + for (const url of getURLsWithCharInHost(char)) { + expect(isInternalURL(url)).toBe(false); + } + } + }); }); describe('without basePath defined', () => { @@ -86,5 +164,83 @@ describe('isInternalURL', () => { const hrefWithThreeSlashes = `///app/kibana`; expect(isInternalURL(hrefWithThreeSlashes)).toBe(false); }); + + it('should properly handle tabs or newline characters in the URL', () => { + // These URLs can either be treated as internal or external depending on the presence of the special characters. + const getURLsWithCharInRelativeScheme = (char: string) => [ + `/${char}/app/kibana`, + `/${char}//app/kibana`, + `/${char}/example.com`, + `/${char}//example.com`, + `/${char}/example.org`, + `/${char}//example.org`, + `/${char}/example.org:5601`, + `/${char}//example.org:5601`, + ]; + + // These URLs can either be treated as internal or external depending on the presence of the special characters + // AND spaces since these affect how URL's scheme is parsed. + const getURLsWithCharInScheme = (char: string) => [ + `htt${char}ps://example.com`, + `htt${char}ps://example.org`, + `htt${char}ps://example.org:5601`, + `java${char}script:alert(1)`, + `htt${char}p://example.org:5601`, + `file${char}://example.com`, + ]; + + // These URLs should always be recognized as external irrespective to the presence of the special characters or + // spaces since these affect URLs hostname. + const getURLsWithCharInHost = (char: string) => [ + `//${char}/app/kibana`, + `//${char}/example.com`, + `//${char}/example.org`, + `//${char}/example.org:5601`, + `https:/${char}/example.com`, + // This URL is only valid if the `char` is a tab or newline character. + `https://example${char}.com/path`, + ]; + + // Detection logic should treat URLs as if tab and newline characters weren't present. + for (const char of ['', '\t', '\n', '\r', '\t\n\r']) { + for (const url of [ + ...getURLsWithCharInRelativeScheme(char), + ...getURLsWithCharInScheme(char), + ...getURLsWithCharInHost(char), + ]) { + expect(isInternalURL(url)).toBe(false); + } + } + + // Characters that aren't tabs, spaces, or newline characters turn absolute scheme-relative URLs to relative URLs. + for (const char of ['1', 'a']) { + for (const url of getURLsWithCharInRelativeScheme(char)) { + expect(isInternalURL(url)).toBe(true); + } + + for (const url of getURLsWithCharInScheme(char)) { + expect(isInternalURL(url)).toBe(false); + } + + for (const url of getURLsWithCharInHost(char)) { + expect(isInternalURL(url)).toBe(false); + } + } + + // Spaces aren't allowed in scheme definition and turn absolute URLs into relative URLs. + for (const char of [' ']) { + for (const url of getURLsWithCharInRelativeScheme(char)) { + expect(isInternalURL(url)).toBe(true); + } + + for (const url of getURLsWithCharInScheme(char)) { + expect(isInternalURL(url)).toBe(true); + } + + for (const url of getURLsWithCharInHost(char)) { + expect(isInternalURL(url)).toBe(false); + } + } + }); }); }); diff --git a/packages/kbn-std/src/is_internal_url.ts b/packages/kbn-std/src/is_internal_url.ts index 434fa6eaf7c27..d6251594aafc0 100644 --- a/packages/kbn-std/src/is_internal_url.ts +++ b/packages/kbn-std/src/is_internal_url.ts @@ -6,37 +6,35 @@ * Side Public License, v 1. */ -import { parse as parseUrl } from 'url'; - /** * Determine if url is outside of this Kibana install. */ export function isInternalURL(url: string, basePath = '') { - const { protocol, hostname, port, pathname } = parseUrl( - url, - false /* parseQueryString */, - true /* slashesDenoteHost */ - ); - - // We should explicitly compare `protocol`, `port` and `hostname` to null to make sure these are not - // detected in the URL at all. For example `hostname` can be empty string for Node URL parser, but - // browser (because of various bwc reasons) processes URL differently (e.g. `///abc.com` - for browser - // hostname is `abc.com`, but for Node hostname is an empty string i.e. everything between schema (`//`) - // and the first slash that belongs to path. - if (protocol !== null || hostname !== null || port !== null) { + // We use the WHATWG parser TWICE with completely different dummy base URLs to ensure that the parsed URL always + // inherits the origin of the base URL. This means that the specified URL isn't an absolute URL, or a scheme-relative + // URL (//), or a scheme-relative URL with an empty host (///). Browsers may process such URLs unexpectedly due to + // backward compatibility reasons (e.g., a browser may treat `///abc.com` as just `abc.com`). For more details, refer + // to https://url.spec.whatwg.org/#concept-basic-url-parser and https://url.spec.whatwg.org/#url-representation. + let normalizedURL: URL; + try { + for (const baseURL of ['http://example.org:5601', 'https://example.com']) { + normalizedURL = new URL(url, baseURL); + if (normalizedURL.origin !== baseURL) { + return false; + } + } + } catch { return false; } + // Now we need to normalize URL to make sure any relative path segments (`..`) cannot escape expected base path. if (basePath) { - // Now we need to normalize URL to make sure any relative path segments (`..`) cannot escape expected - // base path. We can rely on `URL` with a localhost to automatically "normalize" the URL. - const normalizedPathname = new URL(String(pathname), 'https://localhost').pathname; - return ( // Normalized pathname can add a leading slash, but we should also make sure it's included in - // the original URL too - pathname?.startsWith('/') && - (normalizedPathname === basePath || normalizedPathname.startsWith(`${basePath}/`)) + // the original URL too. We can safely use non-null assertion operator here since we know `normalizedURL` is + // always defined, otherwise we would have returned `false` already. + url.startsWith('/') && + (normalizedURL!.pathname === basePath || normalizedURL!.pathname.startsWith(`${basePath}/`)) ); } diff --git a/packages/kbn-std/src/pick.test.ts b/packages/kbn-std/src/pick.test.ts new file mode 100644 index 0000000000000..b58c8b1e9a9f8 --- /dev/null +++ b/packages/kbn-std/src/pick.test.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 { pick } from './pick'; + +describe('pick', () => { + it('works with object created inline', () => { + const obj = { foo: 'bar', hello: 'dolly' }; + + const result = pick(obj, ['foo']); + expect(result).toEqual({ foo: 'bar' }); + }); + + it('works with objects created via Object.create(null)', () => { + const obj = Object.create(null); + Object.assign(obj, { foo: 'bar', hello: 'dolly' }); + + const result = pick(obj, ['foo']); + expect(result).toEqual({ foo: 'bar' }); + }); + + it('does not pick properties from the prototype', () => { + const proto = { prot: 'o' }; + const obj = Object.create(proto); + Object.assign(obj, { foo: 'bar', hello: 'dolly' }); + + const result = pick(obj, ['foo', 'prot']); + expect(result).toEqual({ foo: 'bar' }); + }); +}); diff --git a/packages/kbn-std/src/pick.ts b/packages/kbn-std/src/pick.ts index c8347aadb219a..8d01c80caee6d 100644 --- a/packages/kbn-std/src/pick.ts +++ b/packages/kbn-std/src/pick.ts @@ -8,10 +8,9 @@ export function pick(obj: T, keys: readonly K[]): Pick { return keys.reduce((acc, key) => { - if (obj.hasOwnProperty(key)) { + if (Object.hasOwn(obj, key)) { acc[key] = obj[key]; } - return acc; }, {} as Pick); } diff --git a/packages/kbn-text-based-editor/src/text_based_languages_editor.tsx b/packages/kbn-text-based-editor/src/text_based_languages_editor.tsx index 64dac6802e6fc..fd20d9c2b25ca 100644 --- a/packages/kbn-text-based-editor/src/text_based_languages_editor.tsx +++ b/packages/kbn-text-based-editor/src/text_based_languages_editor.tsx @@ -359,8 +359,8 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ return { cache: fn.cache, memoizedFieldsFromESQL: fn }; }, []); - const esqlCallbacks: ESQLCallbacks = useMemo( - () => ({ + const esqlCallbacks: ESQLCallbacks = useMemo(() => { + const callbacks: ESQLCallbacks = { getSources: async () => { const [remoteIndices, localIndices] = await Promise.all([ getRemoteIndicesList(dataViews), @@ -399,16 +399,16 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ } return policies.map(({ type, query: policyQuery, ...rest }) => rest); }, - }), - [ - dataViews, - expressions, - indexManagementApiService, - esqlFieldsCache, - memoizedFieldsFromESQL, - abortController, - ] - ); + }; + return callbacks; + }, [ + dataViews, + expressions, + indexManagementApiService, + esqlFieldsCache, + memoizedFieldsFromESQL, + abortController, + ]); const parseMessages = useCallback(async () => { if (editorModel.current) { diff --git a/packages/shared-ux/code_editor/impl/__snapshots__/code_editor.test.tsx.snap b/packages/shared-ux/code_editor/impl/__snapshots__/code_editor.test.tsx.snap index df22190992fb3..b4b4fea7b22fd 100644 --- a/packages/shared-ux/code_editor/impl/__snapshots__/code_editor.test.tsx.snap +++ b/packages/shared-ux/code_editor/impl/__snapshots__/code_editor.test.tsx.snap @@ -170,11 +170,13 @@ exports[` is rendered 1`] = ` isVisible={false} onBlur={[Function]} onFocus={[Function]} + onKeyDown={[Function]} onMouseOut={[Function]} onMouseOver={[Function]} > @@ -250,6 +252,7 @@ exports[` is rendered 1`] = ` /> diff --git a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts index 2fa22873190e5..015c517e9a6b8 100644 --- a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts +++ b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts @@ -85,6 +85,7 @@ describe('checking migration metadata changes on all registered SO types', () => "core-usage-stats": "b3c04da317c957741ebcdedfea4524049fdc79ff", "csp-rule-template": "c151324d5f85178169395eecb12bac6b96064654", "dashboard": "211e9ca30f5a95d5f3c27b1bf2b58e6cfa0c9ae9", + "endpoint:unified-user-artifact-manifest": "71c7fcb52c658b21ea2800a6b6a76972ae1c776e", "endpoint:user-artifact-manifest": "1c3533161811a58772e30cdc77bac4631da3ef2b", "enterprise_search_telemetry": "9ac912e1417fc8681e0cd383775382117c9e3d3d", "epm-packages": "f8ee125b57df31fd035dc04ad81aef475fd2f5bd", @@ -111,7 +112,7 @@ describe('checking migration metadata changes on all registered SO types', () => "ingest-agent-policies": "803dc27e106440c41e8f3c3d8ee8bbb0821bcde2", "ingest-download-sources": "279a68147e62e4d8858c09ad1cf03bd5551ce58d", "ingest-outputs": "daafff49255ab700e07491376fe89f04fc998b91", - "ingest-package-policies": "d63e091b2b3cf2eecaa46ae2533bdd5214a983fc", + "ingest-package-policies": "e6da7d0ee2996241ade23b3a7811fe5d3e449cb2", "ingest_manager_settings": "91445219e7115ff0c45d1dabd5d614a80b421797", "inventory-view": "b8683c8e352a286b4aca1ab21003115a4800af83", "kql-telemetry": "93c1d16c1a0dfca9c8842062cf5ef8f62ae401ad", diff --git a/src/core/server/integration_tests/http/cookie_session_storage.test.ts b/src/core/server/integration_tests/http/cookie_session_storage.test.ts index 4a71502db0ccb..192a3daf8fd2d 100644 --- a/src/core/server/integration_tests/http/cookie_session_storage.test.ts +++ b/src/core/server/integration_tests/http/cookie_session_storage.test.ts @@ -134,7 +134,7 @@ describe('Cookie based SessionStorage', () => { const response = await supertest(innerServer.listener).get('/').expect(200); - const cookies = response.get('set-cookie'); + const cookies = response.get('set-cookie')!; expect(cookies).toBeDefined(); expect(cookies).toHaveLength(1); @@ -172,7 +172,7 @@ describe('Cookie based SessionStorage', () => { const response = await supertest(innerServer.listener).get('/').expect(200); - const cookies = response.get('set-cookie'); + const cookies = response.get('set-cookie')!; expect(cookies).toBeDefined(); expect(cookies).toHaveLength(1); @@ -204,7 +204,7 @@ describe('Cookie based SessionStorage', () => { const response = await supertest(innerServer.listener).get('/').expect(200, { value: null }); - const cookies = response.get('set-cookie'); + const cookies = response.get('set-cookie')!; expect(cookies).not.toBeDefined(); }); @@ -237,7 +237,7 @@ describe('Cookie based SessionStorage', () => { .get('/') .expect(200, { value: userData }); - const cookies = response.get('set-cookie'); + const cookies = response.get('set-cookie')!; expect(cookies).toBeDefined(); await delay(sessionDurationMs); @@ -283,7 +283,7 @@ describe('Cookie based SessionStorage', () => { .get('/') .expect(200, { value: userData }); - const cookies = response.get('set-cookie'); + const cookies = response.get('set-cookie')!; expect(cookies).toBeDefined(); const sessionCookie = retrieveSessionCookie(cookies[0]); @@ -418,7 +418,7 @@ describe('Cookie based SessionStorage', () => { const response = await supertest(innerServer.listener).get('/').expect(200); - const cookies = response.get('set-cookie'); + const cookies = response.get('set-cookie')!; const sessionCookie = retrieveSessionCookie(cookies[0]); const response2 = await supertest(innerServer.listener) @@ -475,7 +475,7 @@ describe('Cookie based SessionStorage', () => { const response = await supertest(innerServer.listener).get('/').expect(200); - const cookies = response.get('set-cookie'); + const cookies = response.get('set-cookie')!; expect(cookies).toBeDefined(); expect(cookies).toHaveLength(1); 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 86427a2488e2d..72daedb990dff 100644 --- a/src/core/server/integration_tests/http/versioned_router.test.ts +++ b/src/core/server/integration_tests/http/versioned_router.test.ts @@ -29,7 +29,7 @@ interface AdditionalOptions { describe('Routing versioned requests', () => { let router: IRouter; - let supertest: Supertest.SuperTest; + let supertest: Supertest.Agent; async function setupServer(cliArgs: Partial = {}, options: AdditionalOptions = {}) { logger = loggingSystemMock.create(); diff --git a/src/core/server/integration_tests/metrics/server_collector.test.ts b/src/core/server/integration_tests/metrics/server_collector.test.ts index 6151cd1ebe6bf..481447f599b21 100644 --- a/src/core/server/integration_tests/metrics/server_collector.test.ts +++ b/src/core/server/integration_tests/metrics/server_collector.test.ts @@ -116,7 +116,7 @@ describe('ServerMetricsCollector', () => { // Subscribe to the aborted$ event const waitFor1stAbort = disconnectAborted$.pipe(take(1)).toPromise(); - discoReq1.abort(); + void discoReq1.abort(); // Wait for the aborted$ event await waitFor1stAbort; @@ -132,7 +132,7 @@ describe('ServerMetricsCollector', () => { // Subscribe to the aborted$ event const waitFor2ndAbort = disconnectAborted$.pipe(take(1)).toPromise(); - discoReq2.abort(); + void discoReq2.abort(); // Wait for the aborted$ event await waitFor2ndAbort; diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts index 43f0d03b86552..712ddd4bca932 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts @@ -49,6 +49,7 @@ const previouslyRegisteredTypes = [ 'event-annotation-group', 'endpoint:user-artifact', 'endpoint:user-artifact-manifest', + 'endpoint:unified-user-artifact-manifest', 'enterprise_search_telemetry', 'epm-packages', 'epm-packages-assets', diff --git a/src/core/server/integration_tests/saved_objects/migrations/group5/dot_kibana_split.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group5/dot_kibana_split.test.ts index 22a3b9858599a..dcb40a3b07621 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group5/dot_kibana_split.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group5/dot_kibana_split.test.ts @@ -205,6 +205,7 @@ describe('split .kibana index into multiple system indices', () => { "connector_token", "core-usage-stats", "csp-rule-template", + "endpoint:unified-user-artifact-manifest", "endpoint:user-artifact-manifest", "enterprise_search_telemetry", "epm-packages", diff --git a/src/dev/license_checker/config.ts b/src/dev/license_checker/config.ts index f73d44b46a982..6b79cae3fac7b 100644 --- a/src/dev/license_checker/config.ts +++ b/src/dev/license_checker/config.ts @@ -86,7 +86,7 @@ export const LICENSE_OVERRIDES = { 'jsts@1.6.2': ['Eclipse Distribution License - v 1.0'], // cf. https://github.com/bjornharrtell/jsts '@mapbox/jsonlint-lines-primitives@2.0.2': ['MIT'], // license in readme https://github.com/tmcw/jsonlint '@elastic/ems-client@8.5.1': ['Elastic License 2.0'], - '@elastic/eui@94.3.0': ['SSPL-1.0 OR Elastic License 2.0'], + '@elastic/eui@94.5.0-backport.1': ['SSPL-1.0 OR Elastic License 2.0'], 'language-subtag-registry@0.3.21': ['CC-BY-4.0'], // retired ODC‑By license https://github.com/mattcg/language-subtag-registry 'buffers@0.1.1': ['MIT'], // license in importing module https://www.npmjs.com/package/binary '@bufbuild/protobuf@1.2.1': ['Apache-2.0'], // license (Apache-2.0 AND BSD-3-Clause) diff --git a/src/plugins/dashboard/common/content_management/v1/cm_services.ts b/src/plugins/dashboard/common/content_management/v1/cm_services.ts index e1fa81338a192..6ef1e8085deaa 100644 --- a/src/plugins/dashboard/common/content_management/v1/cm_services.ts +++ b/src/plugins/dashboard/common/content_management/v1/cm_services.ts @@ -11,6 +11,7 @@ import { savedObjectSchema, objectTypeToGetResultSchema, createOptionsSchemas, + updateOptionsSchema, createResultSchema, } from '@kbn/content-management-utils'; @@ -76,6 +77,11 @@ const createOptionsSchema = schema.object({ references: schema.maybe(createOptionsSchemas.references), }); +const dashboardUpdateOptionsSchema = schema.object({ + references: schema.maybe(updateOptionsSchema.references), + mergeAttributes: schema.maybe(updateOptionsSchema.mergeAttributes), +}); + // Content management service definition. // We need it for BWC support between different versions of the content export const serviceDefinition: ServicesDefinition = { @@ -104,7 +110,7 @@ export const serviceDefinition: ServicesDefinition = { update: { in: { options: { - schema: createOptionsSchema, // same schema as "create" + schema: dashboardUpdateOptionsSchema, }, data: { schema: dashboardAttributesSchema, diff --git a/src/plugins/dashboard/common/content_management/v2/types.ts b/src/plugins/dashboard/common/content_management/v2/types.ts index 00859c1f5cae6..dcb45f9b44c54 100644 --- a/src/plugins/dashboard/common/content_management/v2/types.ts +++ b/src/plugins/dashboard/common/content_management/v2/types.ts @@ -32,7 +32,7 @@ export type DashboardCrudTypes = ContentManagementCrudTypes< DashboardContentType, DashboardAttributes, Pick, - Pick, + Pick, { /** Flag to indicate to only search the text on the "title" field */ onlyTitle?: boolean; diff --git a/src/plugins/dashboard/public/services/dashboard_content_management/lib/save_dashboard_state.test.ts b/src/plugins/dashboard/public/services/dashboard_content_management/lib/save_dashboard_state.test.ts index 86221b8b8110a..5783c463deea9 100644 --- a/src/plugins/dashboard/public/services/dashboard_content_management/lib/save_dashboard_state.test.ts +++ b/src/plugins/dashboard/public/services/dashboard_content_management/lib/save_dashboard_state.test.ts @@ -27,7 +27,15 @@ contentManagement.client.create = jest.fn().mockImplementation(({ options }) => if (options.id === undefined) { return { item: { id: 'newlyGeneratedId' } }; } - return { item: { id: options.id } }; + + throw new Error('Update should be used when id is provided'); +}); + +contentManagement.client.update = jest.fn().mockImplementation(({ id }) => { + if (id === undefined) { + throw new Error('Update needs an id'); + } + return { item: { id } }; }); const allServices = { @@ -61,10 +69,8 @@ describe('Save dashboard state', () => { }); expect(result.id).toBe('Boogaloo'); - expect(allServices.contentManagement.client.create).toHaveBeenCalledWith( - expect.objectContaining({ - options: expect.objectContaining({ id: 'Boogaloo', overwrite: true }), - }) + expect(allServices.contentManagement.client.update).toHaveBeenCalledWith( + expect.objectContaining({ id: 'Boogaloo' }) ); expect(allServices.notifications.toasts.addSuccess).toHaveBeenCalledWith({ title: `Dashboard 'BOO' was saved`, @@ -88,7 +94,7 @@ describe('Save dashboard state', () => { expect(result.redirectRequired).toBe(true); expect(allServices.contentManagement.client.create).toHaveBeenCalledWith( expect.objectContaining({ - options: expect.objectContaining({ id: undefined, overwrite: true }), + options: { references: [] }, }) ); expect(allServices.notifications.toasts.addSuccess).toHaveBeenCalledWith({ diff --git a/src/plugins/dashboard/public/services/dashboard_content_management/lib/save_dashboard_state.ts b/src/plugins/dashboard/public/services/dashboard_content_management/lib/save_dashboard_state.ts index 93f535b9b4d40..68de7ab9f5349 100644 --- a/src/plugins/dashboard/public/services/dashboard_content_management/lib/save_dashboard_state.ts +++ b/src/plugins/dashboard/public/services/dashboard_content_management/lib/save_dashboard_state.ts @@ -186,19 +186,33 @@ export const saveDashboardState = async ({ * Save the saved object using the content management */ const idToSaveTo = saveOptions.saveAsCopy ? undefined : lastSavedId; + try { - const result = await contentManagement.client.create< - DashboardCrudTypes['CreateIn'], - DashboardCrudTypes['CreateOut'] - >({ - contentTypeId: DASHBOARD_CONTENT_ID, - data: attributes, - options: { - id: idToSaveTo, - references: allReferences, - overwrite: true, - }, - }); + const result = idToSaveTo + ? await contentManagement.client.update< + DashboardCrudTypes['UpdateIn'], + DashboardCrudTypes['UpdateOut'] + >({ + id: idToSaveTo, + contentTypeId: DASHBOARD_CONTENT_ID, + data: attributes, + options: { + references: allReferences, + /** perform a "full" update instead, where the provided attributes will fully replace the existing ones */ + mergeAttributes: false, + }, + }) + : await contentManagement.client.create< + DashboardCrudTypes['CreateIn'], + DashboardCrudTypes['CreateOut'] + >({ + contentTypeId: DASHBOARD_CONTENT_ID, + data: attributes, + options: { + references: allReferences, + }, + }); + const newId = result.item.id; if (newId) { diff --git a/src/plugins/data/common/search/expressions/eql.ts b/src/plugins/data/common/search/expressions/eql.ts index d60bb6f72480a..d050461ae6b40 100644 --- a/src/plugins/data/common/search/expressions/eql.ts +++ b/src/plugins/data/common/search/expressions/eql.ts @@ -58,6 +58,7 @@ export const getEqlFn = ({ name, type: 'eql_raw_response', inputTypes: ['kibana_context', 'null'], + allowCache: true, help: i18n.translate('data.search.eql.help', { defaultMessage: 'Run Elasticsearch request', }), diff --git a/src/plugins/data/common/search/expressions/esaggs/esaggs_fn.ts b/src/plugins/data/common/search/expressions/esaggs/esaggs_fn.ts index 7b5b2700f72c7..818e4ab72d59f 100644 --- a/src/plugins/data/common/search/expressions/esaggs/esaggs_fn.ts +++ b/src/plugins/data/common/search/expressions/esaggs/esaggs_fn.ts @@ -57,6 +57,7 @@ export const getEsaggsMeta: () => Omit name, type: 'datatable', inputTypes: ['kibana_context', 'null'], + allowCache: true, help: i18n.translate('data.functions.esaggs.help', { defaultMessage: 'Run AggConfig aggregation', }), diff --git a/src/plugins/data/common/search/expressions/esdsl.ts b/src/plugins/data/common/search/expressions/esdsl.ts index 84d301498f2f9..1173ae83ec47a 100644 --- a/src/plugins/data/common/search/expressions/esdsl.ts +++ b/src/plugins/data/common/search/expressions/esdsl.ts @@ -51,6 +51,7 @@ export const getEsdslFn = ({ name, type: 'es_raw_response', inputTypes: ['kibana_context', 'null'], + allowCache: true, help: i18n.translate('data.search.esdsl.help', { defaultMessage: 'Run Elasticsearch request', }), diff --git a/src/plugins/data/common/search/expressions/esql.ts b/src/plugins/data/common/search/expressions/esql.ts index 2303dabb7e59c..6c3929f201458 100644 --- a/src/plugins/data/common/search/expressions/esql.ts +++ b/src/plugins/data/common/search/expressions/esql.ts @@ -75,6 +75,7 @@ export const getEsqlFn = ({ getStartDependencies }: EsqlFnArguments) => { name: 'esql', type: 'datatable', inputTypes: ['kibana_context', 'null'], + allowCache: true, help: i18n.translate('data.search.esql.help', { defaultMessage: 'Queries Elasticsearch using ES|QL.', }), diff --git a/src/plugins/data/common/search/expressions/essql.ts b/src/plugins/data/common/search/expressions/essql.ts index 69d0e7ed9f727..e459aa4fae170 100644 --- a/src/plugins/data/common/search/expressions/essql.ts +++ b/src/plugins/data/common/search/expressions/essql.ts @@ -66,6 +66,7 @@ export const getEssqlFn = ({ getStartDependencies }: EssqlFnArguments) => { name: 'essql', type: 'datatable', inputTypes: ['kibana_context', 'null'], + allowCache: true, help: i18n.translate('data.search.essql.help', { defaultMessage: 'Queries Elasticsearch using Elasticsearch SQL.', }), diff --git a/src/plugins/data_views/server/index_patterns_api_client.ts b/src/plugins/data_views/server/index_patterns_api_client.ts index 849102c46aa91..28c79836cf8e5 100644 --- a/src/plugins/data_views/server/index_patterns_api_client.ts +++ b/src/plugins/data_views/server/index_patterns_api_client.ts @@ -31,6 +31,7 @@ export class IndexPatternsApiServer implements IDataViewsApiClient { allowNoIndex, indexFilter, fields, + includeEmptyFields, }: GetFieldsOptions) { const indexPatterns = new IndexPatternsFetcher(this.esClient, { uiSettingsClient: this.uiSettingsClient, @@ -45,6 +46,7 @@ export class IndexPatternsApiServer implements IDataViewsApiClient { rollupIndex, indexFilter, fields, + includeEmptyFields, }) .catch((err) => { if ( diff --git a/src/plugins/discover/common/utils/sorting/get_default_sort.ts b/src/plugins/discover/common/utils/sorting/get_default_sort.ts index 5e4e38f2d82b9..8a0fb63323803 100644 --- a/src/plugins/discover/common/utils/sorting/get_default_sort.ts +++ b/src/plugins/discover/common/utils/sorting/get_default_sort.ts @@ -12,21 +12,21 @@ import { isSortable } from './get_sort'; /** * use in case the user didn't manually sort. - * the default sort is returned depending on the data view or non for text based queries + * the default sort is returned depending on the data view or non for ES|QL queries */ export function getDefaultSort( dataView: DataView | undefined, defaultSortOrder: string = 'desc', hidingTimeColumn: boolean = false, - isTextBasedQueryMode: boolean + isEsqlMode: boolean ): SortOrder[] { - if (isTextBasedQueryMode) { + if (isEsqlMode) { return []; } if ( dataView?.timeFieldName && - isSortable(dataView.timeFieldName, dataView, isTextBasedQueryMode) && + isSortable(dataView.timeFieldName, dataView, isEsqlMode) && !hidingTimeColumn ) { return [[dataView.timeFieldName, defaultSortOrder]]; diff --git a/src/plugins/discover/common/utils/sorting/get_sort.ts b/src/plugins/discover/common/utils/sorting/get_sort.ts index 82a2f801906cd..f0a9c7b5b2265 100644 --- a/src/plugins/discover/common/utils/sorting/get_sort.ts +++ b/src/plugins/discover/common/utils/sorting/get_sort.ts @@ -14,14 +14,10 @@ export type SortPairObj = Record; export type SortPair = SortOrder | SortPairObj; export type SortInput = SortPair | SortPair[]; -export function isSortable( - fieldName: string, - dataView: DataView, - isTextBasedQueryMode: boolean -): boolean { - if (isTextBasedQueryMode) { - // in-memory sorting is used for text-based queries - // would be great to have a way to determine if a text-based column is sortable +export function isSortable(fieldName: string, dataView: DataView, isEsqlMode: boolean): boolean { + if (isEsqlMode) { + // in-memory sorting is used for ES|QL queries + // would be great to have a way to determine if a ES|QL column is sortable return fieldName !== '_source'; } const field = dataView.getFieldByName(fieldName); @@ -31,18 +27,18 @@ export function isSortable( function createSortObject( sortPair: SortInput, dataView: DataView, - isTextBasedQueryMode: boolean + isEsqlMode: boolean ): SortPairObj | undefined { if ( Array.isArray(sortPair) && sortPair.length === 2 && - isSortable(String(sortPair[0]), dataView, isTextBasedQueryMode) + isSortable(String(sortPair[0]), dataView, isEsqlMode) ) { const [field, direction] = sortPair as SortOrder; return { [field]: direction }; } else if ( isPlainObject(sortPair) && - isSortable(Object.keys(sortPair)[0], dataView, isTextBasedQueryMode) + isSortable(Object.keys(sortPair)[0], dataView, isEsqlMode) ) { return sortPair as SortPairObj; } @@ -59,13 +55,13 @@ export function isLegacySort(sort: SortPair[] | SortPair): sort is SortPair { * @param {array} sort two dimensional array [[fieldToSort, directionToSort]] * or an array of objects [{fieldToSort: directionToSort}] * @param {object} dataView used for determining default sort - * @param {boolean} isTextBasedQueryMode + * @param {boolean} isEsqlMode * @returns Array<{object}> an array of sort objects */ export function getSort( sort: SortPair[] | SortPair, dataView: DataView, - isTextBasedQueryMode: boolean + isEsqlMode: boolean ): SortPairObj[] { if (Array.isArray(sort)) { if (isLegacySort(sort)) { @@ -73,7 +69,7 @@ export function getSort( return [{ [sort[0]]: sort[1] }]; } return sort - .map((sortPair: SortPair) => createSortObject(sortPair, dataView, isTextBasedQueryMode)) + .map((sortPair: SortPair) => createSortObject(sortPair, dataView, isEsqlMode)) .filter((sortPairObj) => typeof sortPairObj === 'object') as SortPairObj[]; } return []; @@ -86,9 +82,9 @@ export function getSort( export function getSortArray( sort: SortInput, dataView: DataView, - isTextBasedQueryMode: boolean + isEsqlMode: boolean ): SortOrder[] { - return getSort(sort, dataView, isTextBasedQueryMode).reduce((acc: SortOrder[], sortPair) => { + return getSort(sort, dataView, isEsqlMode).reduce((acc: SortOrder[], sortPair) => { const entries = Object.entries(sortPair); if (entries && entries[0]) { acc.push(entries[0]); diff --git a/src/plugins/discover/common/utils/sorting/get_sort_for_search_source.ts b/src/plugins/discover/common/utils/sorting/get_sort_for_search_source.ts index d63c75a4f0150..e19372e9f505e 100644 --- a/src/plugins/discover/common/utils/sorting/get_sort_for_search_source.ts +++ b/src/plugins/discover/common/utils/sorting/get_sort_for_search_source.ts @@ -46,7 +46,7 @@ export function getSortForSearchSource({ } const { timeFieldName } = dataView; - const sortPairs = getSort(sort, dataView, false); // text based request is not using search source + const sortPairs = getSort(sort, dataView, false); // ES|QL request is not using search source const sortForSearchSource = sortPairs.map((sortPair: Record) => { if (timeFieldName && sortPair[timeFieldName]) { diff --git a/src/plugins/discover/public/application/main/components/document_explorer_callout/document_explorer_update_callout.test.tsx b/src/plugins/discover/public/application/main/components/document_explorer_callout/document_explorer_update_callout.test.tsx index 3b46dbe1b8bca..d5d46870ef33f 100644 --- a/src/plugins/discover/public/application/main/components/document_explorer_callout/document_explorer_update_callout.test.tsx +++ b/src/plugins/discover/public/application/main/components/document_explorer_callout/document_explorer_update_callout.test.tsx @@ -17,6 +17,8 @@ import { LocalStorageMock } from '../../../../__mocks__/local_storage_mock'; import { DiscoverServices } from '../../../../build_services'; import { discoverServiceMock } from '../../../../__mocks__/services'; import { DiscoverTourProvider } from '../../../../components/discover_tour'; +import { getDiscoverStateMock } from '../../../../__mocks__/discover_state.mock'; +import { DiscoverMainProvider } from '../../state_management/discover_state_provider'; const defaultServices = { ...discoverServiceMock, @@ -62,9 +64,11 @@ describe('Document Explorer Update callout', () => { it('should start a tour when the button is clicked', () => { const result = mountWithIntl( - - - + + + + + ); diff --git a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx index b5f50f2ca9c14..f382f61275b67 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx @@ -46,7 +46,6 @@ import { useInternalStateSelector } from '../../state_management/discover_intern import { useAppStateSelector } from '../../state_management/discover_app_state_container'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; import { FetchStatus } from '../../../types'; -import { RecordRawType } from '../../state_management/discover_data_state_container'; import { DiscoverStateContainer } from '../../state_management/discover_state'; import { useDataState } from '../../hooks/use_data_state'; import { DocTableInfinite } from '../../../../components/doc_table/doc_table_infinite'; @@ -56,7 +55,6 @@ import { DISCOVER_TOUR_STEP_ANCHOR_IDS, DiscoverTourProvider, } from '../../../../components/discover_tour'; -import { getRawRecordType } from '../../utils/get_raw_record_type'; import { getMaxAllowedSampleSize, getAllowedSampleSize, @@ -68,6 +66,7 @@ import { SelectedVSAvailableCallout } from './selected_vs_available_callout'; import { useDiscoverCustomization } from '../../../../customizations'; import { onResizeGridColumn } from '../../../../utils/on_resize_grid_column'; import { useContextualGridCustomisations } from '../../hooks/grid_customisations'; +import { useIsEsqlMode } from '../../hooks/use_is_esql_mode'; const containerStyles = css` position: relative; @@ -123,12 +122,12 @@ function DiscoverDocumentsComponent({ ]; }); const expandedDoc = useInternalStateSelector((state) => state.expandedDoc); - const isTextBasedQuery = useMemo(() => getRawRecordType(query) === RecordRawType.PLAIN, [query]); + const isEsqlMode = useIsEsqlMode(); const useNewFieldsApi = useMemo(() => !uiSettings.get(SEARCH_FIELDS_FROM_SOURCE), [uiSettings]); const hideAnnouncements = useMemo(() => uiSettings.get(HIDE_ANNOUNCEMENTS), [uiSettings]); const isLegacy = useMemo( - () => isLegacyTableEnabled({ uiSettings, isTextBasedQueryMode: isTextBasedQuery }), - [uiSettings, isTextBasedQuery] + () => isLegacyTableEnabled({ uiSettings, isEsqlMode }), + [uiSettings, isEsqlMode] ); const documentState = useDataState(documents$); const isDataLoading = @@ -136,7 +135,7 @@ function DiscoverDocumentsComponent({ documentState.fetchStatus === FetchStatus.PARTIAL; // This is needed to prevent EuiDataGrid pushing onSort because the data view has been switched. - // It's just necessary for non-text-based query lang requests since they don't have a partial result state, that's + // It's just necessary for non ES|QL requests since they don't have a partial result state, that's // considered as loading state in the Component. // 1. When switching the data view, the sorting in the URL is reset to the default sorting of the selected data view. // 2. The new sort param is already available in this component and propagated to the EuiDataGrid. @@ -145,13 +144,12 @@ function DiscoverDocumentsComponent({ // 5. this is propagated to Discover's URL and causes an unwanted change of state to an unsorted state // This solution switches to the loading state in this component when the URL index doesn't match the dataView.id const isDataViewLoading = - useInternalStateSelector((state) => state.isDataViewLoading) && !isTextBasedQuery; + useInternalStateSelector((state) => state.isDataViewLoading) && !isEsqlMode; const isEmptyDataResult = - isTextBasedQuery || !documentState.result || documentState.result.length === 0; + isEsqlMode || !documentState.result || documentState.result.length === 0; const rows = useMemo(() => documentState.result || [], [documentState.result]); const { isMoreDataLoading, totalHits, onFetchMoreRecords } = useFetchMoreRecords({ - isTextBasedQuery, stateContainer, }); @@ -227,10 +225,10 @@ function DiscoverDocumentsComponent({ const columnsMeta: DataTableColumnsMeta | undefined = useMemo( () => - documentState.textBasedQueryColumns - ? getTextBasedColumnsMeta(documentState.textBasedQueryColumns) + documentState.esqlQueryColumns + ? getTextBasedColumnsMeta(documentState.esqlQueryColumns) : undefined, - [documentState.textBasedQueryColumns] + [documentState.esqlQueryColumns] ); const renderDocumentView = useCallback( @@ -269,19 +267,13 @@ function DiscoverDocumentsComponent({ () => ( <> ), - [ - isTextBasedQuery, - currentColumns, - documents?.textBasedQueryColumns, - documentState.interceptedWarnings, - ] + [currentColumns, documents?.esqlQueryColumns, documentState.interceptedWarnings] ); const gridAnnouncementCallout = useMemo(() => { @@ -289,12 +281,12 @@ function DiscoverDocumentsComponent({ return null; } - return !isTextBasedQuery ? ( - + return !isEsqlMode ? ( + ) : null; - }, [hideAnnouncements, isLegacy, isTextBasedQuery]); + }, [hideAnnouncements, isLegacy, isEsqlMode]); const loadingIndicator = useMemo( () => @@ -364,12 +356,12 @@ function DiscoverDocumentsComponent({ isLoading={isDataLoading} searchDescription={savedSearch.description} sharedItemTitle={savedSearch.title} - isPlainRecord={isTextBasedQuery} + isEsqlMode={isEsqlMode} onAddColumn={onAddColumn} onFilter={onAddFilter as DocViewFilterFn} onMoveColumn={onMoveColumn} onRemoveColumn={onRemoveColumn} - onSort={!isTextBasedQuery ? onSort : undefined} + onSort={!isEsqlMode ? onSort : undefined} useNewFieldsApi={useNewFieldsApi} dataTestSubj="discoverDocTable" /> @@ -415,12 +407,12 @@ function DiscoverDocumentsComponent({ rowHeightState={rowHeight} onUpdateRowHeight={onUpdateRowHeight} isSortEnabled={true} - isPlainRecord={isTextBasedQuery} + isPlainRecord={isEsqlMode} rowsPerPageState={rowsPerPage ?? getDefaultRowsPerPage(services.uiSettings)} onUpdateRowsPerPage={onUpdateRowsPerPage} maxAllowedSampleSize={getMaxAllowedSampleSize(services.uiSettings)} sampleSizeState={getAllowedSampleSize(sampleSizeState, services.uiSettings)} - onUpdateSampleSize={!isTextBasedQuery ? onUpdateSampleSize : undefined} + onUpdateSampleSize={!isEsqlMode ? onUpdateSampleSize : undefined} onFieldEdited={onFieldEdited} configRowHeight={uiSettings.get(ROW_HEIGHT_OPTION)} showMultiFields={uiSettings.get(SHOW_MULTIFIELDS)} diff --git a/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.test.tsx b/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.test.tsx index 7ce8f987e5911..d23dfbe4359eb 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.test.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.test.tsx @@ -17,7 +17,6 @@ import { DataDocuments$, DataMain$, DataTotalHits$, - RecordRawType, } from '../../state_management/discover_data_state_container'; import { discoverServiceMock } from '../../../../__mocks__/services'; import { FetchStatus, SidebarToggleState } from '../../../types'; @@ -52,12 +51,12 @@ function getStateContainer(savedSearch?: SavedSearch) { } const mountComponent = async ({ - isPlainRecord = false, + isEsqlMode = false, storage, savedSearch = savedSearchMockWithTimeField, searchSessionId = '123', }: { - isPlainRecord?: boolean; + isEsqlMode?: boolean; isTimeBased?: boolean; storage?: Storage; savedSearch?: SavedSearch; @@ -86,7 +85,6 @@ const mountComponent = async ({ const main$ = new BehaviorSubject({ fetchStatus: FetchStatus.COMPLETE, - recordRawType: isPlainRecord ? RecordRawType.PLAIN : RecordRawType.DOCUMENT, foundDocuments: true, }) as DataMain$; @@ -121,7 +119,6 @@ const mountComponent = async ({ stateContainer.actions.undoSavedSearchChanges = jest.fn(); const props: DiscoverHistogramLayoutProps = { - isPlainRecord, dataView, stateContainer, onFieldEdited: jest.fn(), @@ -176,8 +173,8 @@ describe('Discover histogram layout component', () => { expect(component.isEmptyRender()).toBe(false); }, 10000); - it('should not render null if there is no search session, but isPlainRecord is true', async () => { - const { component } = await mountComponent({ isPlainRecord: true }); + it('should not render null if there is no search session, but isEsqlMode is true', async () => { + const { component } = await mountComponent({ isEsqlMode: true }); expect(component.isEmptyRender()).toBe(false); }); diff --git a/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.tsx b/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.tsx index 207a2263b0e06..68585d7faf5c0 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.tsx @@ -15,6 +15,7 @@ import { useDiscoverHistogram } from './use_discover_histogram'; import { type DiscoverMainContentProps, DiscoverMainContent } from './discover_main_content'; import { useAppStateSelector } from '../../state_management/discover_app_state_container'; import { FetchStatus } from '../../../types'; +import { useIsEsqlMode } from '../../hooks/use_is_esql_mode'; export interface DiscoverHistogramLayoutProps extends DiscoverMainContentProps { container: HTMLElement | null; @@ -25,7 +26,6 @@ const histogramLayoutCss = css` `; export const DiscoverHistogramLayout = ({ - isPlainRecord, dataView, stateContainer, container, @@ -35,11 +35,11 @@ export const DiscoverHistogramLayout = ({ const { dataState } = stateContainer; const searchSessionId = useObservable(stateContainer.searchSessionManager.searchSessionId$); const hideChart = useAppStateSelector((state) => state.hideChart); + const isEsqlMode = useIsEsqlMode(); const unifiedHistogramProps = useDiscoverHistogram({ stateContainer, inspectorAdapters: dataState.inspectorAdapters, hideChart, - isPlainRecord, }); const datatable = useObservable(dataState.data$.documents$); @@ -53,21 +53,21 @@ export const DiscoverHistogramLayout = ({ const table: Datatable | undefined = useMemo(() => { if ( - isPlainRecord && + isEsqlMode && datatable && [FetchStatus.PARTIAL, FetchStatus.COMPLETE].includes(datatable.fetchStatus) ) { return { type: 'datatable' as 'datatable', rows: datatable.result!.map((r) => r.raw), - columns: datatable.textBasedQueryColumns || [], + columns: datatable.esqlQueryColumns || [], }; } - }, [datatable, isPlainRecord]); + }, [datatable, isEsqlMode]); // Initialized when the first search has been requested or - // when in text-based mode since search sessions are not supported - if (!searchSessionId && !isPlainRecord) { + // when in ES|QL mode since search sessions are not supported + if (!searchSessionId && !isEsqlMode) { return null; } @@ -86,7 +86,6 @@ export const DiscoverHistogramLayout = ({ {...mainContentProps} stateContainer={stateContainer} dataView={dataView} - isPlainRecord={isPlainRecord} panelsToggle={panelsToggle} /> diff --git a/src/plugins/discover/public/application/main/components/layout/discover_layout.test.tsx b/src/plugins/discover/public/application/main/components/layout/discover_layout.test.tsx index c1cc257f58cc3..cf3fd26a10d66 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_layout.test.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_layout.test.tsx @@ -25,7 +25,6 @@ import { DataDocuments$, DataMain$, DataTotalHits$, - RecordRawType, } from '../../state_management/discover_data_state_container'; import { createDiscoverServicesMock } from '../../../../__mocks__/services'; import { FetchStatus } from '../../../types'; @@ -51,10 +50,8 @@ async function mountComponent( prevSidebarClosed?: boolean, mountOptions: { attachTo?: HTMLElement } = {}, query?: Query | AggregateQuery, - isPlainRecord?: boolean, main$: DataMain$ = new BehaviorSubject({ fetchStatus: FetchStatus.COMPLETE, - recordRawType: isPlainRecord ? RecordRawType.PLAIN : RecordRawType.DOCUMENT, foundDocuments: true, }) as DataMain$ ) { @@ -185,10 +182,8 @@ describe('Discover component', () => { undefined, undefined, undefined, - undefined, new BehaviorSubject({ fetchStatus: FetchStatus.ERROR, - recordRawType: RecordRawType.DOCUMENT, foundDocuments: false, error: new Error('No results'), }) as DataMain$ diff --git a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx index 7065e511951b6..cc141ce3fd567 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx @@ -5,6 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ + import './discover_layout.scss'; import React, { ReactElement, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { @@ -23,7 +24,7 @@ import { METRIC_TYPE } from '@kbn/analytics'; import classNames from 'classnames'; import { generateFilters } from '@kbn/data-plugin/public'; import { useDragDropContext } from '@kbn/dom-drag-drop'; -import { DataViewField, DataViewType } from '@kbn/data-views-plugin/public'; +import { DataViewType } from '@kbn/data-views-plugin/public'; import { SEARCH_FIELDS_FROM_SOURCE, SHOW_FIELD_STATISTICS, @@ -44,10 +45,9 @@ import { DiscoverSidebarResponsive } from '../sidebar'; import { DiscoverTopNav } from '../top_nav/discover_topnav'; import { getResultState } from '../../utils/get_result_state'; import { DiscoverUninitialized } from '../uninitialized/uninitialized'; -import { DataMainMsg, RecordRawType } from '../../state_management/discover_data_state_container'; +import { DataMainMsg } from '../../state_management/discover_data_state_container'; import { FetchStatus, SidebarToggleState } from '../../../types'; import { useDataState } from '../../hooks/use_data_state'; -import { getRawRecordType } from '../../utils/get_raw_record_type'; import { SavedSearchURLConflictCallout } from '../../../../components/saved_search_url_conflict_callout/saved_search_url_conflict_callout'; import { DiscoverHistogramLayout } from './discover_histogram_layout'; import { ErrorCallout } from '../../../../components/common/error_callout'; @@ -55,6 +55,7 @@ import { addLog } from '../../../../utils/add_log'; import { DiscoverResizableLayout } from './discover_resizable_layout'; import { PanelsToggle, PanelsToggleProps } from '../../../../components/panels_toggle'; import { sendErrorMsg } from '../../hooks/use_saved_search_messages'; +import { useIsEsqlMode } from '../../hooks/use_is_esql_mode'; const SidebarMemoized = React.memo(DiscoverSidebarResponsive); const TopNavMemoized = React.memo(DiscoverTopNav); @@ -84,9 +85,9 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) { state.columns, state.sort, ]); - const isPlainRecord = useMemo(() => getRawRecordType(query) === RecordRawType.PLAIN, [query]); + const isEsqlMode = useIsEsqlMode(); const viewMode: VIEW_MODE = useAppStateSelector((state) => { - if (uiSettings.get(SHOW_FIELD_STATISTICS) !== true || isPlainRecord) + if (uiSettings.get(SHOW_FIELD_STATISTICS) !== true || isEsqlMode) return VIEW_MODE.DOCUMENT_LEVEL; return state.viewMode ?? VIEW_MODE.DOCUMENT_LEVEL; }); @@ -140,13 +141,16 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) { useEffect(() => { return observabilityAIAssistant?.service.setScreenContext({ screenDescription: `The user is looking at the Discover view on the ${ - isPlainRecord ? 'ES|QL' : 'dataView' + isEsqlMode ? 'ES|QL' : 'dataView' } mode. The index pattern is the ${dataView.getIndexPattern()}`, }); - }, [dataView, isPlainRecord, observabilityAIAssistant?.service]); + }, [dataView, isEsqlMode, observabilityAIAssistant?.service]); - const onAddFilter = useCallback( - (field: DataViewField | string, values: unknown, operation: '+' | '-') => { + const onAddFilter = useCallback( + (field, values, operation) => { + if (!field) { + return; + } const fieldName = typeof field === 'string' ? field : field.name; popularizeField(dataView, fieldName, dataViews, capabilities); const newFilters = generateFilters(filterManager, field, values, operation, dataView); @@ -158,36 +162,50 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) { [filterManager, dataView, dataViews, trackUiMetric, capabilities] ); - const onPopulateWhereClause = useCallback( - (field: DataViewField | string, values: unknown, operation: '+' | '-') => { - if (query && isOfAggregateQueryType(query) && 'esql' in query) { - const fieldName = typeof field === 'string' ? field : field.name; - // send the field type for casting - const fieldType = typeof field !== 'string' ? field.type : undefined; - // weird existence logic from Discover components - // in the field it comes the operator _exists_ and in the value the field - // I need to take care of it here but I think it should be handled on the fieldlist instead - const updatedQuery = appendWhereClauseToESQLQuery( - query.esql, - fieldName === '_exists_' ? String(values) : fieldName, - fieldName === '_exists_' ? undefined : values, - fieldName === '_exists_' ? '_exists_' : operation, - fieldType - ); - data.query.queryString.setQuery({ - esql: updatedQuery, - }); - if (trackUiMetric) { - trackUiMetric(METRIC_TYPE.CLICK, 'esql_filter_added'); - } + const getOperator = (fieldName: string, values: unknown, operation: '+' | '-') => { + if (fieldName === '_exists_') { + return 'is_not_null'; + } + if (values == null && operation === '-') { + return 'is_not_null'; + } + + if (values == null && operation === '+') { + return 'is_null'; + } + + return operation; + }; + + const onPopulateWhereClause = useCallback( + (field, values, operation) => { + if (!field || !isOfAggregateQueryType(query)) { + return; + } + const fieldName = typeof field === 'string' ? field : field.name; + // send the field type for casting + const fieldType = typeof field !== 'string' ? field.type : undefined; + // weird existence logic from Discover components + // in the field it comes the operator _exists_ and in the value the field + // I need to take care of it here but I think it should be handled on the fieldlist instead + const updatedQuery = appendWhereClauseToESQLQuery( + query.esql, + fieldName === '_exists_' ? String(values) : fieldName, + fieldName === '_exists_' || values == null ? undefined : values, + getOperator(fieldName, values, operation), + fieldType + ); + data.query.queryString.setQuery({ + esql: updatedQuery, + }); + if (trackUiMetric) { + trackUiMetric(METRIC_TYPE.CLICK, 'esql_filter_added'); } }, [data.query.queryString, query, trackUiMetric] ); - const onFilter = isPlainRecord - ? (onPopulateWhereClause as DocViewFilterFn) - : (onAddFilter as DocViewFilterFn); + const onFilter = isEsqlMode ? onPopulateWhereClause : onAddFilter; const onFieldEdited = useCallback( async ({ removedFieldName }: { removedFieldName?: string } = {}) => { @@ -212,17 +230,17 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) { const contentCentered = resultState === 'uninitialized' || resultState === 'none'; const documentState = useDataState(stateContainer.dataState.data$.documents$); - const textBasedLanguageModeWarning = useMemo(() => { - if (isPlainRecord) { - return documentState.textBasedHeaderWarning; + const esqlModeWarning = useMemo(() => { + if (isEsqlMode) { + return documentState.esqlHeaderWarning; } - }, [documentState.textBasedHeaderWarning, isPlainRecord]); + }, [documentState.esqlHeaderWarning, isEsqlMode]); - const textBasedLanguageModeErrors = useMemo(() => { - if (isPlainRecord) { + const esqlModeErrors = useMemo(() => { + if (isEsqlMode) { return dataState.error; } - }, [dataState.error, isPlainRecord]); + }, [dataState.error, isEsqlMode]); const [sidebarContainer, setSidebarContainer] = useState(null); const [mainContainer, setMainContainer] = useState(null); @@ -262,7 +280,6 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) { return ( <> { describe('DocumentViewModeToggle', () => { - it('should show DocumentViewModeToggle when isPlainRecord is false', async () => { + it('should show DocumentViewModeToggle when not in ES|QL mode', async () => { const component = await mountComponent(); expect(component.find(DiscoverDocuments).prop('viewModeToggle')).toBeDefined(); }); - it('should include DocumentViewModeToggle when isPlainRecord is true', async () => { - const component = await mountComponent({ isPlainRecord: true }); + it('should include DocumentViewModeToggle when in ES|QL mode', async () => { + const component = await mountComponent({ isEsqlMode: true }); expect(component.find(DiscoverDocuments).prop('viewModeToggle')).toBeDefined(); }); diff --git a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx index 701bbdddb6e93..23e4001a39459 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx @@ -22,6 +22,7 @@ import { DiscoverDocuments } from './discover_documents'; import { DOCUMENTS_VIEW_CLICK, FIELD_STATISTICS_VIEW_CLICK } from '../field_stats_table/constants'; import { useAppStateSelector } from '../../state_management/discover_app_state_container'; import type { PanelsToggleProps } from '../../../../components/panels_toggle'; +import { useIsEsqlMode } from '../../hooks/use_is_esql_mode'; const DROP_PROPS = { value: { @@ -38,7 +39,6 @@ const DROP_PROPS = { export interface DiscoverMainContentProps { dataView: DataView; - isPlainRecord: boolean; stateContainer: DiscoverStateContainer; viewMode: VIEW_MODE; onAddFilter: DocViewFilterFn | undefined; @@ -51,7 +51,6 @@ export interface DiscoverMainContentProps { export const DiscoverMainContent = ({ dataView, - isPlainRecord, viewMode, onAddFilter, onFieldEdited, @@ -62,7 +61,7 @@ export const DiscoverMainContent = ({ isChartAvailable, }: DiscoverMainContentProps) => { const { trackUiMetric, dataVisualizer: dataVisualizerService } = useDiscoverServices(); - + const isEsqlMode = useIsEsqlMode(); const shouldShowViewModeToggle = dataVisualizerService !== undefined; const setDiscoverViewMode = useCallback( @@ -86,7 +85,7 @@ export const DiscoverMainContent = ({ return shouldShowViewModeToggle ? ( ); }, [ + shouldShowViewModeToggle, viewMode, - setDiscoverViewMode, - isPlainRecord, + isEsqlMode, stateContainer, + setDiscoverViewMode, panelsToggle, isChartAvailable, - shouldShowViewModeToggle, ]); const showChart = useAppStateSelector((state) => !state.hideChart); @@ -132,7 +131,7 @@ export const DiscoverMainContent = ({ dataView={dataView} onAddFilter={onAddFilter} stateContainer={stateContainer} - onFieldEdited={!isPlainRecord ? onFieldEdited : undefined} + onFieldEdited={!isEsqlMode ? onFieldEdited : undefined} /> ) : ( <> @@ -141,7 +140,7 @@ export const DiscoverMainContent = ({ dataView={dataView} columns={columns} stateContainer={stateContainer} - onAddFilter={!isPlainRecord ? onAddFilter : undefined} + onAddFilter={!isEsqlMode ? onAddFilter : undefined} trackUiMetric={trackUiMetric} /> diff --git a/src/plugins/discover/public/application/main/components/layout/selected_vs_available_callout.test.tsx b/src/plugins/discover/public/application/main/components/layout/selected_vs_available_callout.test.tsx index 4b6b2da23b2c2..b48d12d511abb 100644 --- a/src/plugins/discover/public/application/main/components/layout/selected_vs_available_callout.test.tsx +++ b/src/plugins/discover/public/application/main/components/layout/selected_vs_available_callout.test.tsx @@ -10,45 +10,49 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import type { DatatableColumn } from '@kbn/expressions-plugin/common'; import { SelectedVSAvailableCallout } from './selected_vs_available_callout'; +import { DiscoverMainProvider } from '../../state_management/discover_state_provider'; +import { getDiscoverStateMock } from '../../../../__mocks__/discover_state.mock'; describe('SelectedVSAvailableCallout', () => { - it('should render the callout if isPlainRecord is true and the selected columns are less than the available ones', async () => { + it('should render the callout if in ES|QL mode and the selected columns are less than the available ones', async () => { + const stateContainer = getDiscoverStateMock({}); + stateContainer.appState.update({ query: { esql: 'select *' } }); const component = mountWithIntl( - + + + ); expect(component.find('[data-test-subj="dscSelectedColumnsCallout"]').exists()).toBe(true); }); - it('should not render the callout if isPlainRecord is false', async () => { + it('should not render the callout if not in ES|QL mode', async () => { const component = mountWithIntl( - + + + ); expect(component.find('[data-test-subj="dscSelectedColumnsCallout"]').exists()).toBe(false); }); - it('should not render the callout if isPlainRecord is true but the selected columns are equal with the available ones', async () => { + it('should not render the callout if in ES|QL mode but the selected columns are equal with the available ones', async () => { const component = mountWithIntl( - + + + ); expect(component.find('[data-test-subj="dscSelectedColumnsCallout"]').exists()).toBe(false); }); diff --git a/src/plugins/discover/public/application/main/components/layout/selected_vs_available_callout.tsx b/src/plugins/discover/public/application/main/components/layout/selected_vs_available_callout.tsx index ec05667e69b35..948f4cf27a50e 100644 --- a/src/plugins/discover/public/application/main/components/layout/selected_vs_available_callout.tsx +++ b/src/plugins/discover/public/application/main/components/layout/selected_vs_available_callout.tsx @@ -9,33 +9,34 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiCallOut } from '@elastic/eui'; import type { DatatableColumn } from '@kbn/expressions-plugin/common'; +import { useIsEsqlMode } from '../../hooks/use_is_esql_mode'; interface SelectedVSAvailableCallout { - isPlainRecord: boolean; selectedColumns: string[]; - textBasedQueryColumns?: DatatableColumn[]; + esqlQueryColumns?: DatatableColumn[]; } export const SelectedVSAvailableCallout = ({ - isPlainRecord, - textBasedQueryColumns, + esqlQueryColumns, selectedColumns, }: SelectedVSAvailableCallout) => { + const isEsqlMode = useIsEsqlMode(); + return ( <> - {isPlainRecord && - textBasedQueryColumns && + {isEsqlMode && + esqlQueryColumns && selectedColumns.length > 0 && - selectedColumns.length < textBasedQueryColumns.length && ( + selectedColumns.length < esqlQueryColumns.length && ( { stateContainer = getStateContainer(), inspectorAdapters = { requests: new RequestAdapter() }, hideChart = false, - isPlainRecord = false, }: { stateContainer?: DiscoverStateContainer; inspectorAdapters?: InspectorAdapters; hideChart?: boolean; - isPlainRecord?: boolean; } = {}) => { const initialProps = { stateContainer, inspectorAdapters, hideChart, - isPlainRecord, }; const Wrapper: WrapperComponent = ({ children }) => ( @@ -174,8 +171,10 @@ describe('useDiscoverHistogram', () => { ]); }); - it('should return the isChartLoading params for text based languages', async () => { - const { hook } = await renderUseDiscoverHistogram({ isPlainRecord: true }); + it('should return the isChartLoading params for ES|QL mode', async () => { + const stateContainer = getStateContainer(); + stateContainer.appState.update({ query: { esql: 'from *' } }); + const { hook } = await renderUseDiscoverHistogram(); const isChartLoading = hook.result.current.isChartLoading; expect(isChartLoading).toBe(false); }); @@ -323,7 +322,6 @@ describe('useDiscoverHistogram', () => { expect(stateContainer.dataState.data$.totalHits$.value).not.toEqual({ fetchStatus: FetchStatus.COMPLETE, result: 100, - recordRawType: stateContainer.dataState.data$.totalHits$.value.recordRawType, }); act(() => { hook.result.current.ref(api); @@ -331,7 +329,6 @@ describe('useDiscoverHistogram', () => { expect(stateContainer.dataState.data$.totalHits$.value).toEqual({ fetchStatus: FetchStatus.COMPLETE, result: 100, - recordRawType: stateContainer.dataState.data$.totalHits$.value.recordRawType, }); expect(mockCheckHitCount).toHaveBeenCalledWith(stateContainer.dataState.data$.main$, 100); }); @@ -370,7 +367,6 @@ describe('useDiscoverHistogram', () => { expect(stateContainer.dataState.data$.totalHits$.value).not.toEqual({ fetchStatus: FetchStatus.ERROR, error, - recordRawType: stateContainer.dataState.data$.totalHits$.value.recordRawType, }); act(() => { hook.result.current.ref(api); @@ -379,7 +375,6 @@ describe('useDiscoverHistogram', () => { expect(stateContainer.dataState.data$.totalHits$.value).toEqual({ fetchStatus: FetchStatus.ERROR, error, - recordRawType: stateContainer.dataState.data$.totalHits$.value.recordRawType, }); expect(mockCheckHitCount).not.toHaveBeenCalled(); }); @@ -393,8 +388,9 @@ describe('useDiscoverHistogram', () => { searchSessionId: string; }>(); const stateContainer = getStateContainer(); + stateContainer.appState.update({ query: { esql: 'from *' } }); stateContainer.dataState.fetch$ = fetch$; - const { hook } = await renderUseDiscoverHistogram({ stateContainer, isPlainRecord: true }); + const { hook } = await renderUseDiscoverHistogram({ stateContainer }); act(() => { fetch$.next({ options: { reset: false, fetchMore: false }, diff --git a/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.ts b/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.ts index bf5fbafa95794..385dd432dc3c1 100644 --- a/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.ts +++ b/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.ts @@ -31,7 +31,7 @@ import useObservable from 'react-use/lib/useObservable'; import type { RequestAdapter } from '@kbn/inspector-plugin/common'; import type { DatatableColumn } from '@kbn/expressions-plugin/common'; import type { SavedSearch } from '@kbn/saved-search-plugin/common'; -import type { Filter } from '@kbn/es-query'; +import { Filter } from '@kbn/es-query'; import { useDiscoverCustomization } from '../../../../customizations'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; import { FetchStatus } from '../../../types'; @@ -41,31 +41,28 @@ import type { DiscoverStateContainer } from '../../state_management/discover_sta import { addLog } from '../../../../utils/add_log'; import { useInternalStateSelector } from '../../state_management/discover_internal_state_container'; import type { DiscoverAppState } from '../../state_management/discover_app_state_container'; -import { - DataDocumentsMsg, - RecordRawType, -} from '../../state_management/discover_data_state_container'; +import { DataDocumentsMsg } from '../../state_management/discover_data_state_container'; import { useSavedSearch } from '../../state_management/discover_state_provider'; +import { useIsEsqlMode } from '../../hooks/use_is_esql_mode'; -const EMPTY_TEXT_BASED_COLUMNS: DatatableColumn[] = []; +const EMPTY_ESQL_COLUMNS: DatatableColumn[] = []; const EMPTY_FILTERS: Filter[] = []; export interface UseDiscoverHistogramProps { stateContainer: DiscoverStateContainer; inspectorAdapters: InspectorAdapters; hideChart: boolean | undefined; - isPlainRecord: boolean; } export const useDiscoverHistogram = ({ stateContainer, inspectorAdapters, hideChart, - isPlainRecord, }: UseDiscoverHistogramProps) => { const services = useDiscoverServices(); const savedSearchData$ = stateContainer.dataState.data$; const savedSearchState = useSavedSearch(); + const isEsqlMode = useIsEsqlMode(); /** * API initialization @@ -163,10 +160,8 @@ export const useDiscoverHistogram = ({ useEffect(() => { const subscription = createTotalHitsObservable(unifiedHistogram?.state$)?.subscribe( ({ status, result }) => { - const { recordRawType, result: totalHitsResult } = savedSearchData$.totalHits$.getValue(); - - if (recordRawType === RecordRawType.PLAIN) { - // ignore histogram's total hits updates for text-based records as Discover manages them during docs fetching + if (isEsqlMode) { + // ignore histogram's total hits updates for ES|QL as Discover manages them during docs fetching return; } @@ -176,6 +171,8 @@ export const useDiscoverHistogram = ({ return; } + const { result: totalHitsResult } = savedSearchData$.totalHits$.getValue(); + if ( (status === UnifiedHistogramFetchStatus.loading || status === UnifiedHistogramFetchStatus.uninitialized) && @@ -190,7 +187,6 @@ export const useDiscoverHistogram = ({ savedSearchData$.totalHits$.next({ fetchStatus: status.toString() as FetchStatus, result, - recordRawType, }); if (status !== UnifiedHistogramFetchStatus.complete || typeof result !== 'number') { @@ -206,9 +202,11 @@ export const useDiscoverHistogram = ({ subscription?.unsubscribe(); }; }, [ + isEsqlMode, savedSearchData$.main$, savedSearchData$.totalHits$, setTotalHitsError, + stateContainer.appState, unifiedHistogram?.state$, ]); @@ -224,30 +222,30 @@ export const useDiscoverHistogram = ({ timefilter.getTime() ); - // When in text based language mode, update the data view, query, and + // When in ES|QL mode, update the data view, query, and // columns only when documents are done fetching so the Lens suggestions // don't frequently change, such as when the user modifies the table // columns, which would trigger unnecessary refetches. - const textBasedFetchComplete$ = useMemo( + const esqlFetchComplete$ = useMemo( () => createFetchCompleteObservable(stateContainer), [stateContainer] ); - const [initialTextBasedProps] = useState(() => - getUnifiedHistogramPropsForTextBased({ + const [initialEsqlProps] = useState(() => + getUnifiedHistogramPropsForEsql({ documentsValue: savedSearchData$.documents$.getValue(), savedSearch: stateContainer.savedSearchState.getState(), }) ); const { - dataView: textBasedDataView, - query: textBasedQuery, - columns: textBasedColumns, - } = useObservable(textBasedFetchComplete$, initialTextBasedProps); + dataView: esqlDataView, + query: esqlQuery, + columns: esqlColumns, + } = useObservable(esqlFetchComplete$, initialEsqlProps); useEffect(() => { - if (!isPlainRecord) { + if (!isEsqlMode) { return; } @@ -256,7 +254,7 @@ export const useDiscoverHistogram = ({ setIsSuggestionLoading(true); } }); - const fetchComplete = textBasedFetchComplete$.subscribe(() => { + const fetchComplete = esqlFetchComplete$.subscribe(() => { setIsSuggestionLoading(false); }); @@ -264,7 +262,7 @@ export const useDiscoverHistogram = ({ fetchStart.unsubscribe(); fetchComplete.unsubscribe(); }; - }, [isPlainRecord, stateContainer.dataState.fetch$, textBasedFetchComplete$]); + }, [isEsqlMode, stateContainer.dataState.fetch$, esqlFetchComplete$]); /** * Data fetching @@ -290,17 +288,17 @@ export const useDiscoverHistogram = ({ let fetch$: Observable; - // When in text based language mode, we refetch under two conditions: + // When in ES|QL mode, we refetch under two conditions: // 1. When the current Lens suggestion changes. This syncs the visualization // with the user's selection. // 2. When the documents are done fetching. This is necessary because we don't // have access to the latest columns until after the documents are fetched, // which are required to get the latest Lens suggestion, which would trigger // a refetch anyway and result in multiple unnecessary fetches. - if (isPlainRecord) { + if (isEsqlMode) { fetch$ = merge( createCurrentSuggestionObservable(unifiedHistogram.state$).pipe(map(() => 'lens')), - textBasedFetchComplete$.pipe(map(() => 'discover')) + esqlFetchComplete$.pipe(map(() => 'discover')) ).pipe(debounceTime(50)); } else { fetch$ = stateContainer.dataState.fetch$.pipe( @@ -320,14 +318,14 @@ export const useDiscoverHistogram = ({ }); // triggering the initial request for total hits hook - if (!isPlainRecord && !skipRefetch.current) { + if (!isEsqlMode && !skipRefetch.current) { unifiedHistogram.refetch(); } return () => { subscription.unsubscribe(); }; - }, [isPlainRecord, stateContainer.dataState.fetch$, textBasedFetchComplete$, unifiedHistogram]); + }, [isEsqlMode, stateContainer.dataState.fetch$, esqlFetchComplete$, unifiedHistogram]); const dataView = useInternalStateSelector((state) => state.dataView!); @@ -384,12 +382,12 @@ export const useDiscoverHistogram = ({ ref, getCreationOptions, services, - dataView: isPlainRecord ? textBasedDataView : dataView, - query: isPlainRecord ? textBasedQuery : query, + dataView: isEsqlMode ? esqlDataView : dataView, + query: isEsqlMode ? esqlQuery : query, filters: filtersMemoized, timeRange: timeRangeMemoized, relativeTimeRange, - columns: isPlainRecord ? textBasedColumns : undefined, + columns: isEsqlMode ? esqlColumns : undefined, onFilter: histogramCustomization?.onFilter, onBrushEnd: histogramCustomization?.onBrushEnd, withDefaultActions: histogramCustomization?.withDefaultActions, @@ -397,10 +395,10 @@ export const useDiscoverHistogram = ({ isChartLoading: isSuggestionLoading, // visContext should be in sync with current query externalVisContext: - isPlainRecord && canImportVisContext(savedSearchState?.visContext) + isEsqlMode && canImportVisContext(savedSearchState?.visContext) ? savedSearchState?.visContext : undefined, - onVisContextChanged: isPlainRecord ? onVisContextChanged : undefined, + onVisContextChanged: isEsqlMode ? onVisContextChanged : undefined, }; }; @@ -476,7 +474,7 @@ const createFetchCompleteObservable = (stateContainer: DiscoverStateContainer) = distinctUntilChanged((prev, curr) => prev.fetchStatus === curr.fetchStatus), filter(({ fetchStatus }) => [FetchStatus.COMPLETE, FetchStatus.ERROR].includes(fetchStatus)), map((documentsValue) => { - return getUnifiedHistogramPropsForTextBased({ + return getUnifiedHistogramPropsForEsql({ documentsValue, savedSearch: stateContainer.savedSearchState.getState(), }); @@ -498,14 +496,14 @@ const createCurrentSuggestionObservable = (state$: Observable { const records = esHitsMockWithSort.map((hit) => buildDataTableRecord(hit, dataViewMock)); @@ -43,18 +46,27 @@ describe('useFetchMoreRecords', () => { return stateContainer; }; + const getWrapper = ( + stateContainer: DiscoverStateContainer + ): WrapperComponent => { + return ({ children }) => ( + + <>{children} + + ); + }; + it('should not be allowed if all records are already loaded', async () => { + const stateContainer = getStateContainer({ + fetchStatus: FetchStatus.COMPLETE, + loadedRecordsCount: 3, + totalRecordsCount: 3, + }); const { result: { current }, } = renderHook((props) => useFetchMoreRecords(props), { - initialProps: { - isTextBasedQuery: false, - stateContainer: getStateContainer({ - fetchStatus: FetchStatus.COMPLETE, - loadedRecordsCount: 3, - totalRecordsCount: 3, - }), - }, + wrapper: getWrapper(stateContainer), + initialProps: { stateContainer }, }); expect(current.onFetchMoreRecords).toBeUndefined(); @@ -63,17 +75,16 @@ describe('useFetchMoreRecords', () => { }); it('should be allowed when there are more records to load', async () => { + const stateContainer = getStateContainer({ + fetchStatus: FetchStatus.COMPLETE, + loadedRecordsCount: 3, + totalRecordsCount: 5, + }); const { result: { current }, } = renderHook((props) => useFetchMoreRecords(props), { - initialProps: { - isTextBasedQuery: false, - stateContainer: getStateContainer({ - fetchStatus: FetchStatus.COMPLETE, - loadedRecordsCount: 3, - totalRecordsCount: 5, - }), - }, + wrapper: getWrapper(stateContainer), + initialProps: { stateContainer }, }); expect(current.onFetchMoreRecords).toBeDefined(); expect(current.isMoreDataLoading).toBe(false); @@ -81,17 +92,16 @@ describe('useFetchMoreRecords', () => { }); it('should not be allowed when there is no initial documents', async () => { + const stateContainer = getStateContainer({ + fetchStatus: FetchStatus.COMPLETE, + loadedRecordsCount: 0, + totalRecordsCount: 5, + }); const { result: { current }, } = renderHook((props) => useFetchMoreRecords(props), { - initialProps: { - isTextBasedQuery: false, - stateContainer: getStateContainer({ - fetchStatus: FetchStatus.COMPLETE, - loadedRecordsCount: 0, - totalRecordsCount: 5, - }), - }, + wrapper: getWrapper(stateContainer), + initialProps: { stateContainer }, }); expect(current.onFetchMoreRecords).toBeUndefined(); expect(current.isMoreDataLoading).toBe(false); @@ -99,35 +109,34 @@ describe('useFetchMoreRecords', () => { }); it('should return loading status correctly', async () => { + const stateContainer = getStateContainer({ + fetchStatus: FetchStatus.LOADING_MORE, + loadedRecordsCount: 3, + totalRecordsCount: 5, + }); const { result: { current }, } = renderHook((props) => useFetchMoreRecords(props), { - initialProps: { - isTextBasedQuery: false, - stateContainer: getStateContainer({ - fetchStatus: FetchStatus.LOADING_MORE, - loadedRecordsCount: 3, - totalRecordsCount: 5, - }), - }, + wrapper: getWrapper(stateContainer), + initialProps: { stateContainer }, }); expect(current.onFetchMoreRecords).toBeDefined(); expect(current.isMoreDataLoading).toBe(true); expect(current.totalHits).toBe(5); }); - it('should not be allowed for text-based queries', async () => { + it('should not be allowed for ES|QL queries', async () => { + const stateContainer = getStateContainer({ + fetchStatus: FetchStatus.COMPLETE, + loadedRecordsCount: 3, + totalRecordsCount: 5, + }); + stateContainer.appState.update({ query: { esql: 'from *' } }); const { result: { current }, } = renderHook((props) => useFetchMoreRecords(props), { - initialProps: { - isTextBasedQuery: true, - stateContainer: getStateContainer({ - fetchStatus: FetchStatus.COMPLETE, - loadedRecordsCount: 3, - totalRecordsCount: 5, - }), - }, + wrapper: getWrapper(stateContainer), + initialProps: { stateContainer }, }); expect(current.onFetchMoreRecords).toBeUndefined(); }); diff --git a/src/plugins/discover/public/application/main/components/layout/use_fetch_more_records.ts b/src/plugins/discover/public/application/main/components/layout/use_fetch_more_records.ts index 381ded3fc17d7..b6901c4f29369 100644 --- a/src/plugins/discover/public/application/main/components/layout/use_fetch_more_records.ts +++ b/src/plugins/discover/public/application/main/components/layout/use_fetch_more_records.ts @@ -9,13 +9,13 @@ import { useMemo } from 'react'; import { FetchStatus } from '../../../types'; import { useDataState } from '../../hooks/use_data_state'; +import { useIsEsqlMode } from '../../hooks/use_is_esql_mode'; import type { DiscoverStateContainer } from '../../state_management/discover_state'; /** * Params for the hook */ export interface UseFetchMoreRecordsParams { - isTextBasedQuery: boolean; stateContainer: DiscoverStateContainer; } @@ -30,24 +30,23 @@ export interface UseFetchMoreRecordsResult { /** * Checks if more records can be loaded and returns a handler for it - * @param isTextBasedQuery * @param stateContainer */ export const useFetchMoreRecords = ({ - isTextBasedQuery, stateContainer, }: UseFetchMoreRecordsParams): UseFetchMoreRecordsResult => { const documents$ = stateContainer.dataState.data$.documents$; const totalHits$ = stateContainer.dataState.data$.totalHits$; const documentState = useDataState(documents$); const totalHitsState = useDataState(totalHits$); + const isEsqlMode = useIsEsqlMode(); const rows = documentState.result || []; const isMoreDataLoading = documentState.fetchStatus === FetchStatus.LOADING_MORE; const totalHits = totalHitsState.result || 0; const canFetchMoreRecords = - !isTextBasedQuery && + !isEsqlMode && rows.length > 0 && totalHits > rows.length && Boolean(rows[rows.length - 1].raw.sort?.length); diff --git a/src/plugins/discover/public/application/main/components/no_results/no_results.test.tsx b/src/plugins/discover/public/application/main/components/no_results/no_results.test.tsx index d88eda61d7db0..d1079c5bddf1f 100644 --- a/src/plugins/discover/public/application/main/components/no_results/no_results.test.tsx +++ b/src/plugins/discover/public/application/main/components/no_results/no_results.test.tsx @@ -21,6 +21,7 @@ import { type Filter } from '@kbn/es-query'; import { DiscoverNoResults, DiscoverNoResultsProps } from './no_results'; import { createDiscoverServicesMock } from '../../../../__mocks__/services'; import { getDiscoverStateMock } from '../../../../__mocks__/discover_state.mock'; +import { DiscoverMainProvider } from '../../state_management/discover_state_provider'; jest.spyOn(RxApi, 'lastValueFrom').mockImplementation(async () => ({ rawResponse: { @@ -61,25 +62,28 @@ async function mountAndFindSubjects( > ) { const isTimeBased = props.dataView.isTimeBased(); + const stateContainer = getDiscoverStateMock({ isTimeBased }); let component: ReactWrapper; - await act(async () => { - component = await mountWithIntl( + act(() => { + component = mountWithIntl( - {}} - {...props} - /> + + {}} + {...props} + /> + ); }); await new Promise((resolve) => setTimeout(resolve, 0)); - await act(async () => { - await component!.update(); + act(() => { + component!.update(); }); return { diff --git a/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestions.tsx b/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestions.tsx index f75376d6ad32c..fa679fa00ecef 100644 --- a/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestions.tsx +++ b/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestions.tsx @@ -10,16 +10,9 @@ import React, { useCallback, useState } from 'react'; import { css } from '@emotion/react'; import { EuiEmptyPrompt, EuiButton, EuiSpacer, useEuiTheme } from '@elastic/eui'; import type { DataView } from '@kbn/data-views-plugin/common'; -import { - isOfQueryType, - isOfAggregateQueryType, - type Query, - type AggregateQuery, - type Filter, -} from '@kbn/es-query'; +import { isOfQueryType, type Query, type AggregateQuery, type Filter } from '@kbn/es-query'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; -import { isTextBasedQuery } from '../../../utils/is_text_based_query'; import { NoResultsSuggestionDefault } from './no_results_suggestion_default'; import { NoResultsSuggestionWhenFilters, @@ -31,6 +24,7 @@ import { hasActiveFilter } from '../../layout/utils'; import { useDiscoverServices } from '../../../../../hooks/use_discover_services'; import { useFetchOccurrencesRange, TimeRangeExtendingStatus } from './use_fetch_occurances_range'; import { NoResultsIllustration } from './assets/no_results_illustration'; +import { useIsEsqlMode } from '../../../hooks/use_is_esql_mode'; interface NoResultsSuggestionProps { dataView: DataView; @@ -50,8 +44,8 @@ export const NoResultsSuggestions: React.FC = ({ const { euiTheme } = useEuiTheme(); const services = useDiscoverServices(); const { data, uiSettings, timefilter, toastNotifications } = services; - const hasQuery = - (isOfQueryType(query) && !!query?.query) || (!!query && isOfAggregateQueryType(query)); + const isEsqlMode = useIsEsqlMode(); + const hasQuery = Boolean(isOfQueryType(query) && query.query) || isEsqlMode; const hasFilters = hasActiveFilter(filters); const [timeRangeExtendingStatus, setTimeRangeExtendingStatus] = @@ -148,7 +142,7 @@ export const NoResultsSuggestions: React.FC = ({ } body={body} actions={ - !isTextBasedQuery(query) && isTimeBased ? ( + !isEsqlMode && isTimeBased ? (
{ - const propsWithTextBasedMode = { + const propsWithEsqlMode = { ...props, columns: ['extension', 'bytes'], onAddFilter: undefined, documents$: new BehaviorSubject({ fetchStatus: FetchStatus.COMPLETE, - recordRawType: RecordRawType.PLAIN, result: getDataTableRecords(stubLogstashDataView), - textBasedQueryColumns: [ + esqlQueryColumns: [ { id: '1', name: 'extension', meta: { type: 'text' } }, { id: '2', name: 'bytes', meta: { type: 'number' } }, { id: '3', name: '@timestamp', meta: { type: 'date' } }, ], }) as DataDocuments$, }; - const compInTextBasedMode = await mountComponent(propsWithTextBasedMode, { + const compInEsqlMode = await mountComponent(propsWithEsqlMode, { query: { esql: 'FROM `index`' }, }); await act(async () => { await new Promise((resolve) => setTimeout(resolve, 0)); - compInTextBasedMode.update(); + compInEsqlMode.update(); }); - expect(findTestSubject(compInTextBasedMode, 'indexPattern-add-field_btn').length).toBe(0); + expect(findTestSubject(compInEsqlMode, 'indexPattern-add-field_btn').length).toBe(0); const popularFieldsCount = findTestSubject( - compInTextBasedMode, + compInEsqlMode, 'fieldListGroupedPopularFields-count' ); const selectedFieldsCount = findTestSubject( - compInTextBasedMode, + compInEsqlMode, 'fieldListGroupedSelectedFields-count' ); const availableFieldsCount = findTestSubject( - compInTextBasedMode, + compInEsqlMode, 'fieldListGroupedAvailableFields-count' ); - const emptyFieldsCount = findTestSubject( - compInTextBasedMode, - 'fieldListGroupedEmptyFields-count' - ); - const metaFieldsCount = findTestSubject( - compInTextBasedMode, - 'fieldListGroupedMetaFields-count' - ); + const emptyFieldsCount = findTestSubject(compInEsqlMode, 'fieldListGroupedEmptyFields-count'); + const metaFieldsCount = findTestSubject(compInEsqlMode, 'fieldListGroupedMetaFields-count'); const unmappedFieldsCount = findTestSubject( - compInTextBasedMode, + compInEsqlMode, 'fieldListGroupedUnmappedFields-count' ); @@ -561,7 +553,7 @@ describe('discover responsive sidebar', function () { expect(mockCalcFieldCounts.mock.calls.length).toBe(0); - expect(findTestSubject(compInTextBasedMode, 'fieldListGrouped__ariaDescription').text()).toBe( + expect(findTestSubject(compInEsqlMode, 'fieldListGrouped__ariaDescription').text()).toBe( '2 selected fields. 3 available fields.' ); }); diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx index 67c7bd28f0b0b..23778ada7566a 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx @@ -26,7 +26,6 @@ import { useDiscoverServices } from '../../../../hooks/use_discover_services'; import { AvailableFields$, DataDocuments$, - RecordRawType, } from '../../state_management/discover_data_state_container'; import { calcFieldCounts } from '../../utils/calc_field_counts'; import { FetchStatus, SidebarToggleState } from '../../../types'; @@ -39,6 +38,7 @@ import { } from './lib/sidebar_reducer'; import { useDiscoverCustomization } from '../../../../customizations'; import { useAdditionalFieldGroups } from '../../hooks/sidebar/use_additional_field_groups'; +import { useIsEsqlMode } from '../../hooks/use_is_esql_mode'; const EMPTY_FIELD_COUNTS = {}; @@ -151,6 +151,7 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) useState(null); const { euiTheme } = useEuiTheme(); const services = useDiscoverServices(); + const isEsqlMode = useIsEsqlMode(); const { fieldListVariant, selectedDataView, @@ -174,8 +175,6 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) useEffect(() => { const subscription = props.documents$.subscribe((documentState) => { - const isPlainRecordType = documentState.recordRawType === RecordRawType.PLAIN; - switch (documentState?.fetchStatus) { case FetchStatus.UNINITIALIZED: dispatchSidebarStateAction({ @@ -189,7 +188,7 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) dispatchSidebarStateAction({ type: DiscoverSidebarReducerActionType.DOCUMENTS_LOADING, payload: { - isPlainRecord: isPlainRecordType, + isEsqlMode, }, }); break; @@ -198,11 +197,9 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) type: DiscoverSidebarReducerActionType.DOCUMENTS_LOADED, payload: { dataView: selectedDataViewRef.current, - fieldCounts: isPlainRecordType - ? EMPTY_FIELD_COUNTS - : calcFieldCounts(documentState.result), - textBasedQueryColumns: documentState.textBasedQueryColumns, - isPlainRecord: isPlainRecordType, + fieldCounts: isEsqlMode ? EMPTY_FIELD_COUNTS : calcFieldCounts(documentState.result), + esqlQueryColumns: documentState.esqlQueryColumns, + isEsqlMode, }, }); break; @@ -212,7 +209,7 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) payload: { dataView: selectedDataViewRef.current, fieldCounts: EMPTY_FIELD_COUNTS, - isPlainRecord: isPlainRecordType, + isEsqlMode, }, }); break; @@ -221,7 +218,7 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) } }); return () => subscription.unsubscribe(); - }, [props.documents$, dispatchSidebarStateAction, selectedDataViewRef]); + }, [props.documents$, dispatchSidebarStateAction, selectedDataViewRef, isEsqlMode]); useEffect(() => { if (selectedDataView !== selectedDataViewRef.current) { diff --git a/src/plugins/discover/public/application/main/components/sidebar/lib/get_field_list.ts b/src/plugins/discover/public/application/main/components/sidebar/lib/get_field_list.ts index 071454b0bd579..99d911dd14f61 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/lib/get_field_list.ts +++ b/src/plugins/discover/public/application/main/components/sidebar/lib/get_field_list.ts @@ -61,13 +61,11 @@ export function getDataViewFieldList( return [...dataViewFields, ...unknownFields]; } -export function getTextBasedQueryFieldList( - textBasedQueryColumns?: DatatableColumn[] -): DataViewField[] { - if (!textBasedQueryColumns) { +export function getEsqlQueryFieldList(esqlQueryColumns?: DatatableColumn[]): DataViewField[] { + if (!esqlQueryColumns) { return []; } - return textBasedQueryColumns.map( + return esqlQueryColumns.map( (column) => new DataViewField({ name: column.name, diff --git a/src/plugins/discover/public/application/main/components/sidebar/lib/sidebar_reducer.test.ts b/src/plugins/discover/public/application/main/components/sidebar/lib/sidebar_reducer.test.ts index 8c6ce4888fb47..d1135192091bf 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/lib/sidebar_reducer.test.ts +++ b/src/plugins/discover/public/application/main/components/sidebar/lib/sidebar_reducer.test.ts @@ -40,7 +40,7 @@ describe('sidebar reducer', function () { const resultForDocuments = discoverSidebarReducer(state, { type: DiscoverSidebarReducerActionType.DOCUMENTS_LOADING, payload: { - isPlainRecord: false, + isEsqlMode: false, }, }); expect(resultForDocuments).toEqual( @@ -51,13 +51,13 @@ describe('sidebar reducer', function () { status: DiscoverSidebarReducerStatus.PROCESSING, }) ); - const resultForTextBasedQuery = discoverSidebarReducer(state, { + const resultForEsqlQuery = discoverSidebarReducer(state, { type: DiscoverSidebarReducerActionType.DOCUMENTS_LOADING, payload: { - isPlainRecord: true, + isEsqlMode: true, }, }); - expect(resultForTextBasedQuery).toEqual( + expect(resultForEsqlQuery).toEqual( expect.objectContaining({ dataView, allFields: null, @@ -75,7 +75,7 @@ describe('sidebar reducer', function () { const resultForDocuments = discoverSidebarReducer(state, { type: DiscoverSidebarReducerActionType.DOCUMENTS_LOADED, payload: { - isPlainRecord: false, + isEsqlMode: false, dataView: stubDataViewWithoutTimeField, fieldCounts, }, @@ -96,13 +96,13 @@ describe('sidebar reducer', function () { status: DiscoverSidebarReducerStatus.COMPLETED, }); - const resultForTextBasedQuery = discoverSidebarReducer(state, { + const resultForEsqlQuery = discoverSidebarReducer(state, { type: DiscoverSidebarReducerActionType.DOCUMENTS_LOADED, payload: { - isPlainRecord: true, + isEsqlMode: true, dataView: stubDataViewWithoutTimeField, fieldCounts: {}, - textBasedQueryColumns: [ + esqlQueryColumns: [ { id: '1', name: 'text1', @@ -122,7 +122,7 @@ describe('sidebar reducer', function () { ] as DatatableColumn[], }, }); - expect(resultForTextBasedQuery).toStrictEqual({ + expect(resultForEsqlQuery).toStrictEqual({ dataView: stubDataViewWithoutTimeField, allFields: [ new DataViewField({ @@ -149,7 +149,7 @@ describe('sidebar reducer', function () { const resultWhileLoading = discoverSidebarReducer(state, { type: DiscoverSidebarReducerActionType.DOCUMENTS_LOADED, payload: { - isPlainRecord: false, + isEsqlMode: false, dataView: stubDataViewWithoutTimeField, fieldCounts: null, }, diff --git a/src/plugins/discover/public/application/main/components/sidebar/lib/sidebar_reducer.ts b/src/plugins/discover/public/application/main/components/sidebar/lib/sidebar_reducer.ts index 54e9a2c95ce12..e771ed9a19a52 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/lib/sidebar_reducer.ts +++ b/src/plugins/discover/public/application/main/components/sidebar/lib/sidebar_reducer.ts @@ -8,7 +8,7 @@ import { type DataView, type DataViewField } from '@kbn/data-views-plugin/common'; import type { DatatableColumn } from '@kbn/expressions-plugin/common'; -import { getDataViewFieldList, getTextBasedQueryFieldList } from './get_field_list'; +import { getDataViewFieldList, getEsqlQueryFieldList } from './get_field_list'; export enum DiscoverSidebarReducerActionType { RESET = 'RESET', @@ -33,15 +33,15 @@ type DiscoverSidebarReducerAction = | { type: DiscoverSidebarReducerActionType.DOCUMENTS_LOADING; payload: { - isPlainRecord: boolean; + isEsqlMode: boolean; }; } | { type: DiscoverSidebarReducerActionType.DOCUMENTS_LOADED; payload: { fieldCounts: DiscoverSidebarReducerState['fieldCounts']; - textBasedQueryColumns?: DatatableColumn[]; // from text-based searches - isPlainRecord: boolean; + esqlQueryColumns?: DatatableColumn[]; // from ES|QL searches + isEsqlMode: boolean; dataView: DataView | null | undefined; }; }; @@ -92,12 +92,12 @@ export function discoverSidebarReducer( return { ...state, fieldCounts: null, - allFields: action.payload.isPlainRecord ? null : state.allFields, + allFields: action.payload.isEsqlMode ? null : state.allFields, status: DiscoverSidebarReducerStatus.PROCESSING, }; case DiscoverSidebarReducerActionType.DOCUMENTS_LOADED: - const mappedAndUnmappedFields = action.payload.isPlainRecord - ? getTextBasedQueryFieldList(action.payload.textBasedQueryColumns) + const mappedAndUnmappedFields = action.payload.isEsqlMode + ? getEsqlQueryFieldList(action.payload.esqlQueryColumns) : getDataViewFieldList(action.payload.dataView, action.payload.fieldCounts); return { ...state, diff --git a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx index 61acdd1875a14..d05226a0098ce 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx @@ -23,14 +23,14 @@ import { onSaveSearch } from './on_save_search'; import { useDiscoverCustomization } from '../../../../customizations'; import { addLog } from '../../../../utils/add_log'; import { useAppStateSelector } from '../../state_management/discover_app_state_container'; -import { isTextBasedQuery } from '../../utils/is_text_based_query'; import { useDiscoverTopNav } from './use_discover_topnav'; +import { useIsEsqlMode } from '../../hooks/use_is_esql_mode'; export interface DiscoverTopNavProps { savedQuery?: string; stateContainer: DiscoverStateContainer; - textBasedLanguageModeErrors?: Error; - textBasedLanguageModeWarning?: string; + esqlModeErrors?: Error; + esqlModeWarning?: string; onFieldEdited: () => Promise; isLoading?: boolean; onCancelClick?: () => void; @@ -39,8 +39,8 @@ export interface DiscoverTopNavProps { export const DiscoverTopNav = ({ savedQuery, stateContainer, - textBasedLanguageModeErrors, - textBasedLanguageModeWarning, + esqlModeErrors, + esqlModeWarning, onFieldEdited, isLoading, onCancelClick, @@ -60,14 +60,13 @@ export const DiscoverTopNav = ({ const dataView = useInternalStateSelector((state) => state.dataView!); const savedDataViews = useInternalStateSelector((state) => state.savedDataViews); const savedSearch = useSavedSearchInitial(); + const isEsqlMode = useIsEsqlMode(); const showDatePicker = useMemo(() => { - // always show the timepicker for text based languages - const isTextBased = isTextBasedQuery(query); + // always show the timepicker for ES|QL mode return ( - isTextBased || - (!isTextBased && dataView.isTimeBased() && dataView.type !== DataViewType.ROLLUP) + isEsqlMode || (!isEsqlMode && dataView.isTimeBased() && dataView.type !== DataViewType.ROLLUP) ); - }, [dataView, query]); + }, [dataView, isEsqlMode]); const closeFieldEditor = useRef<() => void | undefined>(); const closeDataViewEditor = useRef<() => void | undefined>(); @@ -151,7 +150,7 @@ export const DiscoverTopNav = ({ } }; - const onTextBasedSavedAndExit = useCallback( + const onEsqlSavedAndExit = useCallback( ({ onSave, onCancel }) => { onSaveSearch({ savedSearch: stateContainer.savedSearchState.getState(), @@ -257,11 +256,9 @@ export const DiscoverTopNav = ({ shouldHideDefaultDataviewPicker ? undefined : dataViewPickerProps } displayStyle="detached" - textBasedLanguageModeErrors={ - textBasedLanguageModeErrors ? [textBasedLanguageModeErrors] : undefined - } - textBasedLanguageModeWarning={textBasedLanguageModeWarning} - onTextBasedSavedAndExit={onTextBasedSavedAndExit} + textBasedLanguageModeErrors={esqlModeErrors ? [esqlModeErrors] : undefined} + textBasedLanguageModeWarning={esqlModeWarning} + onTextBasedSavedAndExit={onEsqlSavedAndExit} prependFilterBar={ searchBarCustomization?.PrependFilterBar ? ( diff --git a/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.test.ts b/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.test.ts index 9b5451558529f..2faf12d62bf0a 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.test.ts +++ b/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.test.ts @@ -27,7 +27,7 @@ test('getTopNavLinks result', () => { onOpenInspector: jest.fn(), services, state, - isTextBased: false, + isEsqlMode: false, adHocDataViews: [], topNavCustomization: undefined, }); @@ -80,7 +80,7 @@ test('getTopNavLinks result for ES|QL mode', () => { onOpenInspector: jest.fn(), services, state, - isTextBased: true, + isEsqlMode: true, adHocDataViews: [], topNavCustomization: undefined, }); diff --git a/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.tsx b/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.tsx index ce1592c1598d3..a25e10ce3b0be 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.tsx @@ -28,7 +28,7 @@ export const getTopNavLinks = ({ services, state, onOpenInspector, - isTextBased, + isEsqlMode, adHocDataViews, topNavCustomization, }: { @@ -36,7 +36,7 @@ export const getTopNavLinks = ({ services: DiscoverServices; state: DiscoverStateContainer; onOpenInspector: () => void; - isTextBased: boolean; + isEsqlMode: boolean; adHocDataViews: DataView[]; topNavCustomization: TopNavCustomization | undefined; }): TopNavMenuData[] => { @@ -54,7 +54,7 @@ export const getTopNavLinks = ({ services, stateContainer: state, adHocDataViews, - isPlainRecord: isTextBased, + isEsqlMode, }); }, testId: 'discoverAlertsButton', @@ -127,7 +127,7 @@ export const getTopNavLinks = ({ savedSearch.searchSource, state.appState.getState(), services, - isTextBased + isEsqlMode ); const { locator, notifications } = services; @@ -189,7 +189,7 @@ export const getTopNavLinks = ({ }), }, sharingData: { - isTextBased, + isTextBased: isEsqlMode, locatorParams: [{ id: locator.id, params }], ...searchSourceSharingData, // CSV reports can be generated without a saved search so we provide a fallback title diff --git a/src/plugins/discover/public/application/main/components/top_nav/on_save_search.tsx b/src/plugins/discover/public/application/main/components/top_nav/on_save_search.tsx index 57bcf6baa4e8d..a2ecbe1f8123f 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/on_save_search.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/on_save_search.tsx @@ -10,13 +10,13 @@ import React, { useState } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFormRow, EuiSwitch } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { isOfAggregateQueryType } from '@kbn/es-query'; import { SavedObjectSaveModal, showSaveModal, OnSaveProps } from '@kbn/saved-objects-plugin/public'; import { SavedSearch, SaveSavedSearchOptions } from '@kbn/saved-search-plugin/public'; import { isLegacyTableEnabled } from '@kbn/discover-utils'; import { DiscoverServices } from '../../../../build_services'; import { DiscoverStateContainer } from '../../state_management/discover_state'; import { getAllowedSampleSize } from '../../../../utils/get_allowed_sample_size'; +import { DataSourceType, isDataSourceType } from '../../../../../common/data_sources'; async function saveDataSource({ savedSearch, @@ -114,6 +114,7 @@ export async function onSaveSearch({ isTitleDuplicateConfirmed: boolean; onTitleDuplicate: () => void; }) => { + const appState = state.appState.getState(); const currentTitle = savedSearch.title; const currentTimeRestore = savedSearch.timeRestore; const currentRowsPerPage = savedSearch.rowsPerPage; @@ -121,18 +122,19 @@ export async function onSaveSearch({ const currentDescription = savedSearch.description; const currentTags = savedSearch.tags; const currentVisContext = savedSearch.visContext; + savedSearch.title = newTitle; savedSearch.description = newDescription; savedSearch.timeRestore = newTimeRestore; savedSearch.rowsPerPage = isLegacyTableEnabled({ uiSettings, - isTextBasedQueryMode: isOfAggregateQueryType(savedSearch.searchSource.getField('query')), + isEsqlMode: isDataSourceType(appState.dataSource, DataSourceType.Esql), }) ? currentRowsPerPage - : state.appState.getState().rowsPerPage; + : appState.rowsPerPage; // save the custom value or reset it if it's invalid - const appStateSampleSize = state.appState.getState().sampleSize; + const appStateSampleSize = appState.sampleSize; const allowedSampleSize = getAllowedSampleSize(appStateSampleSize, uiSettings); savedSearch.sampleSize = appStateSampleSize && allowedSampleSize === appStateSampleSize @@ -165,6 +167,7 @@ export async function onSaveSearch({ state, navigateOrReloadSavedSearch, }); + // If the save wasn't successful, put the original values back. if (!response) { savedSearch.title = currentTitle; @@ -180,7 +183,9 @@ export async function onSaveSearch({ state.internalState.transitions.resetOnSavedSearchChange(); state.appState.resetInitialState(); } + onSaveCb?.(); + return response; }; @@ -199,6 +204,7 @@ export async function onSaveSearch({ onClose={onClose ?? (() => {})} /> ); + showSaveModal(saveModal); } diff --git a/src/plugins/discover/public/application/main/components/top_nav/open_alerts_popover.test.tsx b/src/plugins/discover/public/application/main/components/top_nav/open_alerts_popover.test.tsx index bea933950200d..0ef82691681e1 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/open_alerts_popover.test.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/open_alerts_popover.test.tsx @@ -17,7 +17,7 @@ import { dataViewWithNoTimefieldMock } from '../../../../__mocks__/data_view_no_ import { dataViewMock } from '@kbn/discover-utils/src/__mocks__'; import { getDiscoverStateMock } from '../../../../__mocks__/discover_state.mock'; -const mount = (dataView = dataViewMock, isPlainRecord = false) => { +const mount = (dataView = dataViewMock, isEsqlMode = false) => { const stateContainer = getDiscoverStateMock({ isTimeBased: true }); stateContainer.actions.setDataView(dataView); return mountWithIntl( @@ -26,7 +26,7 @@ const mount = (dataView = dataViewMock, isPlainRecord = false) => { stateContainer={stateContainer} anchorElement={document.createElement('div')} adHocDataViews={[]} - isPlainRecord={isPlainRecord} + isEsqlMode={isEsqlMode} services={discoverServiceMock} onClose={jest.fn()} /> diff --git a/src/plugins/discover/public/application/main/components/top_nav/open_alerts_popover.tsx b/src/plugins/discover/public/application/main/components/top_nav/open_alerts_popover.tsx index 36bea3fb21506..097278ac9cd66 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/open_alerts_popover.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/open_alerts_popover.tsx @@ -40,7 +40,7 @@ interface AlertsPopoverProps { savedQueryId?: string; adHocDataViews: DataView[]; services: DiscoverServices; - isPlainRecord?: boolean; + isEsqlMode?: boolean; } interface EsQueryAlertMetaData extends RuleTypeMetaData { @@ -54,7 +54,7 @@ export function AlertsPopover({ services, stateContainer, onClose: originalOnClose, - isPlainRecord, + isEsqlMode, }: AlertsPopoverProps) { const dataView = stateContainer.internalState.getState().dataView; const query = stateContainer.appState.getState().query; @@ -72,7 +72,7 @@ export function AlertsPopover({ * Provides the default parameters used to initialize the new rule */ const getParams = useCallback(() => { - if (isPlainRecord) { + if (isEsqlMode) { return { searchType: 'esqlQuery', esqlQuery: query, @@ -87,7 +87,7 @@ export function AlertsPopover({ .searchSource.getSerializedFields(), savedQueryId, }; - }, [isPlainRecord, stateContainer.appState, stateContainer.savedSearchState, query, timeField]); + }, [isEsqlMode, stateContainer.appState, stateContainer.savedSearchState, query, timeField]); const discoverMetadata: EsQueryAlertMetaData = useMemo( () => ({ @@ -128,12 +128,12 @@ export function AlertsPopover({ }, [alertFlyoutVisible, triggersActionsUi, discoverMetadata, getParams, onClose, stateContainer]); const hasTimeFieldName: boolean = useMemo(() => { - if (!isPlainRecord) { + if (!isEsqlMode) { return Boolean(dataView?.timeFieldName); } else { return Boolean(timeField); } - }, [dataView?.timeFieldName, isPlainRecord, timeField]); + }, [dataView?.timeFieldName, isEsqlMode, timeField]); const panels = [ { @@ -201,13 +201,13 @@ export function openAlertsPopover({ stateContainer, services, adHocDataViews, - isPlainRecord, + isEsqlMode, }: { anchorElement: HTMLElement; stateContainer: DiscoverStateContainer; services: DiscoverServices; adHocDataViews: DataView[]; - isPlainRecord?: boolean; + isEsqlMode?: boolean; }) { if (isOpen) { closeAlertsPopover(); @@ -226,7 +226,7 @@ export function openAlertsPopover({ stateContainer={stateContainer} adHocDataViews={adHocDataViews} services={services} - isPlainRecord={isPlainRecord} + isEsqlMode={isEsqlMode} /> diff --git a/src/plugins/discover/public/application/main/components/top_nav/use_discover_topnav.ts b/src/plugins/discover/public/application/main/components/top_nav/use_discover_topnav.ts index 58eda4f292674..06efd3205d1be 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/use_discover_topnav.ts +++ b/src/plugins/discover/public/application/main/components/top_nav/use_discover_topnav.ts @@ -11,10 +11,9 @@ import useObservable from 'react-use/lib/useObservable'; import { useDiscoverCustomization } from '../../../../customizations'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; import { useInspector } from '../../hooks/use_inspector'; -import { useAppStateSelector } from '../../state_management/discover_app_state_container'; +import { useIsEsqlMode } from '../../hooks/use_is_esql_mode'; import { useInternalStateSelector } from '../../state_management/discover_internal_state_container'; import type { DiscoverStateContainer } from '../../state_management/discover_state'; -import { isTextBasedQuery } from '../../utils/is_text_based_query'; import { getTopNavBadges } from './get_top_nav_badges'; import { getTopNavLinks } from './get_top_nav_links'; @@ -43,8 +42,7 @@ export const useDiscoverTopNav = ({ const dataView = useInternalStateSelector((state) => state.dataView); const adHocDataViews = useInternalStateSelector((state) => state.adHocDataViews); - const query = useAppStateSelector((state) => state.query); - const isTextBased = useMemo(() => isTextBasedQuery(query), [query]); + const isEsqlMode = useIsEsqlMode(); const onOpenInspector = useInspector({ inspector: services.inspector, stateContainer, @@ -57,14 +55,14 @@ export const useDiscoverTopNav = ({ services, state: stateContainer, onOpenInspector, - isTextBased, + isEsqlMode, adHocDataViews, topNavCustomization, }), [ adHocDataViews, dataView, - isTextBased, + isEsqlMode, onOpenInspector, services, stateContainer, diff --git a/src/plugins/discover/public/application/main/data_fetching/fetch_all.test.ts b/src/plugins/discover/public/application/main/data_fetching/fetch_all.test.ts index e41047dedb887..d6999c2200436 100644 --- a/src/plugins/discover/public/application/main/data_fetching/fetch_all.test.ts +++ b/src/plugins/discover/public/application/main/data_fetching/fetch_all.test.ts @@ -18,11 +18,10 @@ import { DataDocumentsMsg, DataMainMsg, DataTotalHitsMsg, - RecordRawType, SavedSearchData, } from '../state_management/discover_data_state_container'; import { fetchDocuments } from './fetch_documents'; -import { fetchTextBased } from './fetch_text_based'; +import { fetchEsql } from './fetch_esql'; import { buildDataTableRecord } from '@kbn/discover-utils'; import { dataViewMock, esHitsMockWithSort } from '@kbn/discover-utils/src/__mocks__'; import { searchResponseIncompleteWarningLocalCluster } from '@kbn/search-response-warnings/src/__mocks__/search_response_warnings'; @@ -31,12 +30,12 @@ jest.mock('./fetch_documents', () => ({ fetchDocuments: jest.fn().mockResolvedValue([]), })); -jest.mock('./fetch_text_based', () => ({ - fetchTextBased: jest.fn().mockResolvedValue([]), +jest.mock('./fetch_esql', () => ({ + fetchEsql: jest.fn().mockResolvedValue([]), })); const mockFetchDocuments = fetchDocuments as unknown as jest.MockedFunction; -const mockfetchTextBased = fetchTextBased as unknown as jest.MockedFunction; +const mockfetchEsql = fetchEsql as unknown as jest.MockedFunction; function subjectCollector(subject: Subject): () => Promise { const promise = firstValueFrom( @@ -90,7 +89,7 @@ describe('test fetchAll', () => { }; mockFetchDocuments.mockReset().mockResolvedValue({ records: [] }); - mockfetchTextBased.mockReset().mockResolvedValue({ records: [] }); + mockfetchEsql.mockReset().mockResolvedValue({ records: [] }); }); test('changes of fetchStatus when starting with FetchStatus.UNINITIALIZED', async () => { @@ -120,10 +119,9 @@ describe('test fetchAll', () => { await waitForNextTick(); expect(await collect()).toEqual([ { fetchStatus: FetchStatus.UNINITIALIZED }, - { fetchStatus: FetchStatus.LOADING, recordRawType: 'document' }, + { fetchStatus: FetchStatus.LOADING }, { fetchStatus: FetchStatus.COMPLETE, - recordRawType: 'document', result: documents, }, ]); @@ -141,21 +139,19 @@ describe('test fetchAll', () => { subjects.totalHits$.next({ fetchStatus: FetchStatus.LOADING, - recordRawType: RecordRawType.DOCUMENT, }); fetchAll(subjects, false, deps); await waitForNextTick(); subjects.totalHits$.next({ fetchStatus: FetchStatus.COMPLETE, - recordRawType: RecordRawType.DOCUMENT, result: 42, }); expect(await collect()).toEqual([ { fetchStatus: FetchStatus.UNINITIALIZED }, - { fetchStatus: FetchStatus.LOADING, recordRawType: 'document' }, - { fetchStatus: FetchStatus.PARTIAL, recordRawType: 'document', result: 2 }, - { fetchStatus: FetchStatus.COMPLETE, recordRawType: 'document', result: 42 }, + { fetchStatus: FetchStatus.LOADING }, + { fetchStatus: FetchStatus.PARTIAL, result: 2 }, + { fetchStatus: FetchStatus.COMPLETE, result: 42 }, ]); }); @@ -164,21 +160,19 @@ describe('test fetchAll', () => { searchSource.getField('index')!.isTimeBased = () => true; subjects.totalHits$.next({ fetchStatus: FetchStatus.LOADING, - recordRawType: RecordRawType.DOCUMENT, }); fetchAll(subjects, false, deps); await waitForNextTick(); subjects.totalHits$.next({ fetchStatus: FetchStatus.COMPLETE, - recordRawType: RecordRawType.DOCUMENT, result: 32, }); expect(await collect()).toEqual([ { fetchStatus: FetchStatus.UNINITIALIZED }, - { fetchStatus: FetchStatus.LOADING, recordRawType: 'document' }, - { fetchStatus: FetchStatus.PARTIAL, recordRawType: 'document', result: 0 }, // From documents query - { fetchStatus: FetchStatus.COMPLETE, recordRawType: 'document', result: 32 }, + { fetchStatus: FetchStatus.LOADING }, + { fetchStatus: FetchStatus.PARTIAL, result: 0 }, // From documents query + { fetchStatus: FetchStatus.COMPLETE, result: 32 }, ]); }); @@ -191,31 +185,28 @@ describe('test fetchAll', () => { mockFetchDocuments.mockResolvedValue({ records: documents }); subjects.totalHits$.next({ fetchStatus: FetchStatus.LOADING, - recordRawType: RecordRawType.DOCUMENT, }); fetchAll(subjects, false, deps); await waitForNextTick(); subjects.totalHits$.next({ fetchStatus: FetchStatus.ERROR, - recordRawType: RecordRawType.DOCUMENT, error: { msg: 'Oh noes!' } as unknown as Error, }); expect(await collectTotalHits()).toEqual([ { fetchStatus: FetchStatus.UNINITIALIZED }, - { fetchStatus: FetchStatus.LOADING, recordRawType: 'document' }, - { fetchStatus: FetchStatus.PARTIAL, recordRawType: 'document', result: 1 }, - { fetchStatus: FetchStatus.ERROR, recordRawType: 'document', error: { msg: 'Oh noes!' } }, + { fetchStatus: FetchStatus.LOADING }, + { fetchStatus: FetchStatus.PARTIAL, result: 1 }, + { fetchStatus: FetchStatus.ERROR, error: { msg: 'Oh noes!' } }, ]); expect(await collectMain()).toEqual([ { fetchStatus: FetchStatus.UNINITIALIZED }, - { fetchStatus: FetchStatus.LOADING, recordRawType: 'document' }, - { fetchStatus: FetchStatus.PARTIAL, recordRawType: 'document' }, + { fetchStatus: FetchStatus.LOADING }, + { fetchStatus: FetchStatus.PARTIAL }, { fetchStatus: FetchStatus.COMPLETE, foundDocuments: true, error: undefined, - recordRawType: 'document', }, ]); }); @@ -226,23 +217,20 @@ describe('test fetchAll', () => { mockFetchDocuments.mockRejectedValue({ msg: 'This query failed' }); subjects.totalHits$.next({ fetchStatus: FetchStatus.LOADING, - recordRawType: RecordRawType.DOCUMENT, }); fetchAll(subjects, false, deps); await waitForNextTick(); subjects.totalHits$.next({ fetchStatus: FetchStatus.COMPLETE, - recordRawType: RecordRawType.DOCUMENT, result: 5, }); expect(await collectMain()).toEqual([ { fetchStatus: FetchStatus.UNINITIALIZED }, - { fetchStatus: FetchStatus.LOADING, recordRawType: 'document' }, + { fetchStatus: FetchStatus.LOADING }, { fetchStatus: FetchStatus.ERROR, error: { msg: 'This query failed' }, - recordRawType: 'document', }, // Here should be no COMPLETE coming anymore ]); @@ -255,9 +243,9 @@ describe('test fetchAll', () => { { _id: '2', _index: 'logs' }, ]; const documents = hits.map((hit) => buildDataTableRecord(hit, dataViewMock)); - mockfetchTextBased.mockResolvedValue({ + mockfetchEsql.mockResolvedValue({ records: documents, - textBasedQueryColumns: [{ id: '1', name: 'test1', meta: { type: 'number' } }], + esqlQueryColumns: [{ id: '1', name: 'test1', meta: { type: 'number' } }], }); const query = { esql: 'from foo' }; deps = { @@ -284,12 +272,11 @@ describe('test fetchAll', () => { expect(await collect()).toEqual([ { fetchStatus: FetchStatus.UNINITIALIZED }, - { fetchStatus: FetchStatus.LOADING, recordRawType: 'plain', query }, + { fetchStatus: FetchStatus.LOADING, query }, { fetchStatus: FetchStatus.PARTIAL, - recordRawType: 'plain', result: documents, - textBasedQueryColumns: [{ id: '1', name: 'test1', meta: { type: 'number' } }], + esqlQueryColumns: [{ id: '1', name: 'test1', meta: { type: 'number' } }], query, }, ]); @@ -308,7 +295,6 @@ describe('test fetchAll', () => { mockFetchDocuments.mockResolvedValue({ records: moreRecords, interceptedWarnings }); subjects.documents$.next({ fetchStatus: FetchStatus.COMPLETE, - recordRawType: RecordRawType.DOCUMENT, result: initialRecords, }); fetchMoreDocuments(subjects, deps); @@ -318,17 +304,14 @@ describe('test fetchAll', () => { { fetchStatus: FetchStatus.UNINITIALIZED }, { fetchStatus: FetchStatus.COMPLETE, - recordRawType: RecordRawType.DOCUMENT, result: initialRecords, }, { fetchStatus: FetchStatus.LOADING_MORE, - recordRawType: RecordRawType.DOCUMENT, result: initialRecords, }, { fetchStatus: FetchStatus.COMPLETE, - recordRawType: RecordRawType.DOCUMENT, result: [...initialRecords, ...moreRecords], interceptedWarnings, }, @@ -346,7 +329,6 @@ describe('test fetchAll', () => { mockFetchDocuments.mockRejectedValue({ msg: 'This query failed' }); subjects.documents$.next({ fetchStatus: FetchStatus.COMPLETE, - recordRawType: RecordRawType.DOCUMENT, result: initialRecords, }); fetchMoreDocuments(subjects, deps); @@ -356,17 +338,14 @@ describe('test fetchAll', () => { { fetchStatus: FetchStatus.UNINITIALIZED }, { fetchStatus: FetchStatus.COMPLETE, - recordRawType: RecordRawType.DOCUMENT, result: initialRecords, }, { fetchStatus: FetchStatus.LOADING_MORE, - recordRawType: RecordRawType.DOCUMENT, result: initialRecords, }, { fetchStatus: FetchStatus.COMPLETE, - recordRawType: RecordRawType.DOCUMENT, result: initialRecords, }, ]); @@ -385,7 +364,7 @@ describe('test fetchAll', () => { test('should swallow abort errors', async () => { const collect = subjectCollector(subjects.documents$); - mockfetchTextBased.mockRejectedValue({ msg: 'The query was aborted' }); + mockfetchEsql.mockRejectedValue({ msg: 'The query was aborted' }); const query = { esql: 'from foo' }; deps = { abortController: new AbortController(), diff --git a/src/plugins/discover/public/application/main/data_fetching/fetch_all.ts b/src/plugins/discover/public/application/main/data_fetching/fetch_all.ts index 2d003680904d6..410d1d468275d 100644 --- a/src/plugins/discover/public/application/main/data_fetching/fetch_all.ts +++ b/src/plugins/discover/public/application/main/data_fetching/fetch_all.ts @@ -5,14 +5,15 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ + import { Adapters } from '@kbn/inspector-plugin/common'; import type { SavedSearch, SortOrder } from '@kbn/saved-search-plugin/public'; import { BehaviorSubject, filter, firstValueFrom, map, merge, scan } from 'rxjs'; import { reportPerformanceMetricEvent } from '@kbn/ebt-tools'; import { isEqual } from 'lodash'; +import { isOfAggregateQueryType } from '@kbn/es-query'; import type { DiscoverAppState } from '../state_management/discover_app_state_container'; import { updateVolatileSearchSource } from './update_search_source'; -import { getRawRecordType } from '../utils/get_raw_record_type'; import { checkHitCount, sendCompleteMsg, @@ -25,13 +26,9 @@ import { } from '../hooks/use_saved_search_messages'; import { fetchDocuments } from './fetch_documents'; import { FetchStatus } from '../../types'; -import { - DataMsg, - RecordRawType, - SavedSearchData, -} from '../state_management/discover_data_state_container'; +import { DataMsg, SavedSearchData } from '../state_management/discover_data_state_container'; import { DiscoverServices } from '../../../build_services'; -import { fetchTextBased } from './fetch_text_based'; +import { fetchEsql } from './fetch_esql'; import { InternalState } from '../state_management/discover_internal_state_container'; export interface FetchDeps { @@ -74,13 +71,13 @@ export function fetchAll( const dataView = searchSource.getField('index')!; const query = getAppState().query; const prevQuery = dataSubjects.documents$.getValue().query; - const recordRawType = getRawRecordType(query); - const useTextBased = recordRawType === RecordRawType.PLAIN; + const isEsqlQuery = isOfAggregateQueryType(query); + if (reset) { - sendResetMsg(dataSubjects, initialFetchStatus, recordRawType); + sendResetMsg(dataSubjects, initialFetchStatus); } - if (recordRawType === RecordRawType.DOCUMENT) { + if (!isEsqlQuery) { // Update the base searchSource, base for all child fetches updateVolatileSearchSource(searchSource, { dataView, @@ -90,23 +87,20 @@ export function fetchAll( }); } - const shouldFetchTextBased = useTextBased && !!query; - // Mark all subjects as loading - sendLoadingMsg(dataSubjects.main$, { recordRawType }); - sendLoadingMsg(dataSubjects.documents$, { recordRawType, query }); + sendLoadingMsg(dataSubjects.main$); + sendLoadingMsg(dataSubjects.documents$, { query }); // histogram for data view mode will send `loading` for totalHits$ - if (shouldFetchTextBased) { + if (isEsqlQuery) { sendLoadingMsg(dataSubjects.totalHits$, { - recordRawType, result: dataSubjects.totalHits$.getValue().result, }); } // Start fetching all required requests - const response = shouldFetchTextBased - ? fetchTextBased( + const response = isEsqlQuery + ? fetchEsql( query, dataView, data, @@ -115,11 +109,12 @@ export function fetchAll( abortController.signal ) : fetchDocuments(searchSource, fetchDeps); - const fetchType = shouldFetchTextBased ? 'fetchTextBased' : 'fetchDocuments'; + const fetchType = isEsqlQuery ? 'fetchTextBased' : 'fetchDocuments'; const startTime = window.performance.now(); + // Handle results of the individual queries and forward the results to the corresponding dataSubjects response - .then(({ records, textBasedQueryColumns, interceptedWarnings, textBasedHeaderWarning }) => { + .then(({ records, esqlQueryColumns, interceptedWarnings, esqlHeaderWarning }) => { if (services.analytics) { const duration = window.performance.now() - startTime; reportPerformanceMetricEvent(services.analytics, { @@ -129,11 +124,10 @@ export function fetchAll( }); } - if (shouldFetchTextBased) { + if (isEsqlQuery) { dataSubjects.totalHits$.next({ fetchStatus: FetchStatus.COMPLETE, result: records.length, - recordRawType, }); } else { const currentTotalHits = dataSubjects.totalHits$.getValue(); @@ -144,29 +138,28 @@ export function fetchAll( dataSubjects.totalHits$.next({ fetchStatus: FetchStatus.PARTIAL, result: records.length, - recordRawType, }); } } + /** - * The partial state for text based query languages is necessary in case the query has changed - * In the follow up useTextBasedQueryLanguage hook in this case new columns are added to AppState + * The partial state for ES|QL mode is necessary in case the query has changed + * In the follow up useEsqlMode hook in this case new columns are added to AppState * So the data table shows the new columns of the table. The partial state was introduced to prevent * To frequent change of state causing the table to re-render to often, which causes race conditions * So it takes too long, a bad user experience, also a potential flakniess in tests */ const fetchStatus = - useTextBased && (!prevQuery || !isEqual(query, prevQuery)) + isEsqlQuery && (!prevQuery || !isEqual(query, prevQuery)) ? FetchStatus.PARTIAL : FetchStatus.COMPLETE; dataSubjects.documents$.next({ fetchStatus, result: records, - textBasedQueryColumns, - textBasedHeaderWarning, + esqlQueryColumns, + esqlHeaderWarning, interceptedWarnings, - recordRawType, query, }); @@ -210,12 +203,11 @@ export async function fetchMoreDocuments( try { const { getAppState, getInternalState, services, savedSearch } = fetchDeps; const searchSource = savedSearch.searchSource.createChild(); - const dataView = searchSource.getField('index')!; const query = getAppState().query; - const recordRawType = getRawRecordType(query); + const isEsqlQuery = isOfAggregateQueryType(query); - if (recordRawType === RecordRawType.PLAIN) { + if (isEsqlQuery) { // not supported yet return; } diff --git a/src/plugins/discover/public/application/main/data_fetching/fetch_text_based.ts b/src/plugins/discover/public/application/main/data_fetching/fetch_esql.ts similarity index 80% rename from src/plugins/discover/public/application/main/data_fetching/fetch_text_based.ts rename to src/plugins/discover/public/application/main/data_fetching/fetch_esql.ts index 7045eb3c28cbc..3aba795d26920 100644 --- a/src/plugins/discover/public/application/main/data_fetching/fetch_text_based.ts +++ b/src/plugins/discover/public/application/main/data_fetching/fetch_esql.ts @@ -18,14 +18,14 @@ import { textBasedQueryStateToAstWithValidation } from '@kbn/data-plugin/common' import type { DataTableRecord } from '@kbn/discover-utils/types'; import type { RecordsFetchResponse } from '../../types'; -interface TextBasedErrorResponse { +interface EsqlErrorResponse { error: { message: string; }; type: 'error'; } -export function fetchTextBased( +export function fetchEsql( query: Query | AggregateQuery, dataView: DataView, data: DataPublicPluginStart, @@ -42,10 +42,10 @@ export function fetchTextBased( time: timeRange, dataView, inputQuery, - titleForInspector: i18n.translate('discover.inspectorTextBasedRequestTitle', { + titleForInspector: i18n.translate('discover.inspectorEsqlRequestTitle', { defaultMessage: 'Table', }), - descriptionForInspector: i18n.translate('discover.inspectorTextBasedRequestDescription', { + descriptionForInspector: i18n.translate('discover.inspectorEsqlRequestDescription', { defaultMessage: 'This request queries Elasticsearch to fetch results for the table.', }), }) @@ -57,18 +57,18 @@ export function fetchTextBased( abortSignal?.addEventListener('abort', contract.cancel); const execution = contract.getData(); let finalData: DataTableRecord[] = []; - let textBasedQueryColumns: Datatable['columns'] | undefined; + let esqlQueryColumns: Datatable['columns'] | undefined; let error: string | undefined; - let textBasedHeaderWarning: string | undefined; + let esqlHeaderWarning: string | undefined; execution.pipe(pluck('result')).subscribe((resp) => { - const response = resp as Datatable | TextBasedErrorResponse; + const response = resp as Datatable | EsqlErrorResponse; if (response.type === 'error') { error = response.error.message; } else { const table = response as Datatable; const rows = table?.rows ?? []; - textBasedQueryColumns = table?.columns ?? undefined; - textBasedHeaderWarning = table.warning ?? undefined; + esqlQueryColumns = table?.columns ?? undefined; + esqlHeaderWarning = table.warning ?? undefined; finalData = rows.map((row: Record, idx: number) => { return { id: String(idx), @@ -84,16 +84,16 @@ export function fetchTextBased( } else { return { records: finalData || [], - textBasedQueryColumns, - textBasedHeaderWarning, + esqlQueryColumns, + esqlHeaderWarning, }; } }); } return { records: [] as DataTableRecord[], - textBasedQueryColumns: [], - textBasedHeaderWarning: undefined, + esqlQueryColumns: [], + esqlHeaderWarning: undefined, }; }) .catch((err) => { diff --git a/src/plugins/discover/public/application/main/discover_main_app.tsx b/src/plugins/discover/public/application/main/discover_main_app.tsx index 7872aa5440304..e59be8cb10185 100644 --- a/src/plugins/discover/public/application/main/discover_main_app.tsx +++ b/src/plugins/discover/public/application/main/discover_main_app.tsx @@ -17,7 +17,7 @@ import { useDiscoverServices } from '../../hooks/use_discover_services'; import { useSavedSearchAliasMatchRedirect } from '../../hooks/saved_search_alias_match_redirect'; import { useSavedSearchInitial } from './state_management/discover_state_provider'; import { useAdHocDataViews } from './hooks/use_adhoc_data_views'; -import { useTextBasedQueryLanguage } from './hooks/use_text_based_query_language'; +import { useEsqlMode } from './hooks/use_esql_mode'; import { addLog } from '../../utils/add_log'; const DiscoverLayoutMemoized = React.memo(DiscoverLayout); @@ -45,10 +45,11 @@ export function DiscoverMainApp(props: DiscoverMainProps) { /** * State changes (data view, columns), when a text base query result is returned */ - useTextBasedQueryLanguage({ + useEsqlMode({ dataViews: services.dataViews, stateContainer, }); + /** * Start state syncing and fetch data if necessary */ diff --git a/src/plugins/discover/public/application/main/discover_main_route.tsx b/src/plugins/discover/public/application/main/discover_main_route.tsx index b0c5ff4bc534f..560f4cb03535e 100644 --- a/src/plugins/discover/public/application/main/discover_main_route.tsx +++ b/src/plugins/discover/public/application/main/discover_main_route.tsx @@ -19,7 +19,6 @@ import { getSavedSearchFullPathUrl } from '@kbn/saved-search-plugin/public'; import useObservable from 'react-use/lib/useObservable'; import { reportPerformanceMetricEvent } from '@kbn/ebt-tools'; import { withSuspense } from '@kbn/shared-ux-utility'; -import { isOfAggregateQueryType } from '@kbn/es-query'; import { getInitialESQLQuery } from '@kbn/esql-utils'; import { ESQL_TYPE } from '@kbn/data-view-utils'; import { useUrl } from './hooks/use_url'; @@ -39,8 +38,8 @@ import { useDiscoverCustomizationService, } from '../../customizations'; import { DiscoverTopNavInline } from './components/top_nav/discover_topnav_inline'; -import { isTextBasedQuery } from './utils/is_text_based_query'; import { DiscoverStateContainer, LoadParams } from './state_management/discover_state'; +import { DataSourceType, isDataSourceType } from '../../../common/data_sources'; const DiscoverMainAppMemoized = memo(DiscoverMainApp); @@ -78,7 +77,6 @@ export function DiscoverMainRoute({ customizationContext, stateStorageContainer, }); - const { customizationService, isInitialized: isCustomizationServiceInitialized } = useDiscoverCustomizationService({ customizationCallbacks, @@ -116,7 +114,7 @@ export function DiscoverMainRoute({ return true; // bypass NoData screen } - if (isOfAggregateQueryType(stateContainer.appState.getState().query)) { + if (isDataSourceType(stateContainer.appState.getState().dataSource, DataSourceType.Esql)) { return true; } @@ -369,8 +367,7 @@ function getLoadParamsForNewSearch(stateContainer: DiscoverStateContainer): { const prevAppState = stateContainer.appState.getState(); const prevDataView = stateContainer.internalState.getState().dataView; const initialAppState = - prevAppState?.query && - isTextBasedQuery(prevAppState.query) && + isDataSourceType(prevAppState.dataSource, DataSourceType.Esql) && prevDataView && prevDataView.type === ESQL_TYPE ? { diff --git a/src/plugins/discover/public/application/main/hooks/use_adhoc_data_views.ts b/src/plugins/discover/public/application/main/hooks/use_adhoc_data_views.ts index 9bb13020533f7..71a42b7d394a2 100644 --- a/src/plugins/discover/public/application/main/hooks/use_adhoc_data_views.ts +++ b/src/plugins/discover/public/application/main/hooks/use_adhoc_data_views.ts @@ -10,12 +10,11 @@ import { useEffect } from 'react'; import { METRIC_TYPE } from '@kbn/analytics'; import { DiscoverServices } from '../../../build_services'; import { useSavedSearch } from '../state_management/discover_state_provider'; -import { isTextBasedQuery } from '../utils/is_text_based_query'; -import { useAppStateSelector } from '../state_management/discover_app_state_container'; import { useInternalStateSelector } from '../state_management/discover_internal_state_container'; import { ADHOC_DATA_VIEW_RENDER_EVENT } from '../../../constants'; import { DiscoverStateContainer } from '../state_management/discover_state'; import { useFiltersValidation } from './use_filters_validation'; +import { useIsEsqlMode } from './use_is_esql_mode'; export const useAdHocDataViews = ({ services, @@ -23,17 +22,16 @@ export const useAdHocDataViews = ({ stateContainer: DiscoverStateContainer; services: DiscoverServices; }) => { - const query = useAppStateSelector((state) => state.query); const dataView = useInternalStateSelector((state) => state.dataView); const savedSearch = useSavedSearch(); - const isTextBasedMode = isTextBasedQuery(query); + const isEsqlMode = useIsEsqlMode(); const { filterManager, toastNotifications } = services; useEffect(() => { if (dataView && !dataView.isPersisted()) { services.trackUiMetric?.(METRIC_TYPE.COUNT, ADHOC_DATA_VIEW_RENDER_EVENT); } - }, [dataView, isTextBasedMode, services]); + }, [dataView, isEsqlMode, services]); /** * Takes care of checking data view id references in filters diff --git a/src/plugins/discover/public/application/main/hooks/use_test_based_query_language.test.tsx b/src/plugins/discover/public/application/main/hooks/use_esql_mode.test.tsx similarity index 87% rename from src/plugins/discover/public/application/main/hooks/use_test_based_query_language.test.tsx rename to src/plugins/discover/public/application/main/hooks/use_esql_mode.test.tsx index 8777b357714ba..5ae16f0429288 100644 --- a/src/plugins/discover/public/application/main/hooks/use_test_based_query_language.test.tsx +++ b/src/plugins/discover/public/application/main/hooks/use_esql_mode.test.tsx @@ -10,9 +10,8 @@ import { renderHook } from '@testing-library/react-hooks'; import { waitFor } from '@testing-library/react'; import { DataViewsContract } from '@kbn/data-plugin/public'; import { discoverServiceMock } from '../../../__mocks__/services'; -import { useTextBasedQueryLanguage } from './use_text_based_query_language'; +import { useEsqlMode } from './use_esql_mode'; import { FetchStatus } from '../../types'; -import { RecordRawType } from '../state_management/discover_data_state_container'; import type { DataTableRecord } from '@kbn/discover-utils/types'; import { AggregateQuery, Query } from '@kbn/es-query'; import { dataViewMock } from '@kbn/discover-utils/src/__mocks__'; @@ -37,7 +36,6 @@ function getHookProps( stateContainer.internalState.transitions.setSavedDataViews([dataViewMock as DataViewListItem]); const msgLoading = { - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.PARTIAL, query, }; @@ -52,7 +50,6 @@ function getHookProps( } const query = { esql: 'from the-data-view-title' }; const msgComplete = { - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.PARTIAL, result: [ { @@ -92,14 +89,14 @@ const renderHookWithContext = ( }); } - renderHook(() => useTextBasedQueryLanguage(props), { + renderHook(() => useEsqlMode(props), { wrapper: getHookContext(props.stateContainer), }); return props; }; -describe('useTextBasedQueryLanguage', () => { - test('a text based query should change state when loading and finished', async () => { +describe('useEsqlMode', () => { + test('an ES|QL query should change state when loading and finished', async () => { const { replaceUrlState, stateContainer } = renderHookWithContext(true); replaceUrlState.mockReset(); @@ -117,14 +114,13 @@ describe('useTextBasedQueryLanguage', () => { viewMode: undefined, }); }); - test('changing a text based query with different result columns should change state when loading and finished', async () => { + test('changing an ES|QL query with different result columns should change state when loading and finished', async () => { const { replaceUrlState, stateContainer } = renderHookWithContext(false); const documents$ = stateContainer.dataState.data$.documents$; stateContainer.dataState.data$.documents$.next(msgComplete); replaceUrlState.mockReset(); documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.PARTIAL, result: [ { @@ -145,13 +141,12 @@ describe('useTextBasedQueryLanguage', () => { }); }); - test('changing a text based query with same result columns should not change state when loading and finished', async () => { + test('changing an ES|QL query with same result columns should not change state when loading and finished', async () => { const { replaceUrlState, stateContainer } = renderHookWithContext(false); const documents$ = stateContainer.dataState.data$.documents$; stateContainer.dataState.data$.documents$.next(msgComplete); documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.PARTIAL, result: [ { @@ -165,7 +160,7 @@ describe('useTextBasedQueryLanguage', () => { await waitFor(() => expect(replaceUrlState).toHaveBeenCalledTimes(0)); }); - test('only changing a text based query with same result columns should not change columns', async () => { + test('only changing an ES|QL query with same result columns should not change columns', async () => { const { replaceUrlState, stateContainer } = renderHookWithContext(false); const documents$ = stateContainer.dataState.data$.documents$; @@ -175,7 +170,6 @@ describe('useTextBasedQueryLanguage', () => { replaceUrlState.mockReset(); documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.PARTIAL, result: [ { @@ -195,7 +189,6 @@ describe('useTextBasedQueryLanguage', () => { replaceUrlState.mockReset(); documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.PARTIAL, result: [ { @@ -209,7 +202,7 @@ describe('useTextBasedQueryLanguage', () => { await waitFor(() => expect(replaceUrlState).toHaveBeenCalledTimes(0)); }); - test('if its not a text based query coming along, it should be ignored', async () => { + test('if its not an ES|QL query coming along, it should be ignored', async () => { const { replaceUrlState, stateContainer } = renderHookWithContext(false); const documents$ = stateContainer.dataState.data$.documents$; @@ -218,7 +211,6 @@ describe('useTextBasedQueryLanguage', () => { replaceUrlState.mockReset(); documents$.next({ - recordRawType: RecordRawType.DOCUMENT, fetchStatus: FetchStatus.PARTIAL, result: [ { @@ -230,7 +222,6 @@ describe('useTextBasedQueryLanguage', () => { }); documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.PARTIAL, result: [ { @@ -257,7 +248,6 @@ describe('useTextBasedQueryLanguage', () => { expect(replaceUrlState).toHaveBeenCalledTimes(0); documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.PARTIAL, result: [ { @@ -272,7 +262,6 @@ describe('useTextBasedQueryLanguage', () => { expect(replaceUrlState).toHaveBeenCalledTimes(0); documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.PARTIAL, result: [ { @@ -296,7 +285,6 @@ describe('useTextBasedQueryLanguage', () => { const documents$ = stateContainer.dataState.data$.documents$; documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.PARTIAL, result: [ { @@ -316,7 +304,6 @@ describe('useTextBasedQueryLanguage', () => { const documents$ = stateContainer.dataState.data$.documents$; documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.PARTIAL, result: [ { @@ -329,7 +316,6 @@ describe('useTextBasedQueryLanguage', () => { }); expect(replaceUrlState).toHaveBeenCalledTimes(0); documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.PARTIAL, result: [ { @@ -353,13 +339,11 @@ describe('useTextBasedQueryLanguage', () => { const documents$ = stateContainer.dataState.data$.documents$; documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.LOADING, query: { esql: 'from the-data-view-title | WHERE field1=2' }, }); expect(replaceUrlState).toHaveBeenCalledTimes(0); documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.PARTIAL, result: [ { @@ -377,24 +361,20 @@ describe('useTextBasedQueryLanguage', () => { replaceUrlState.mockReset(); documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.LOADING, query: { esql: 'from the-data-view-title | keep field 1; | WHERE field1=2' }, }); documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.ERROR, }); documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.LOADING, query: { esql: 'from the-data-view-title | keep field1' }, }); documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.PARTIAL, result: [ { @@ -412,20 +392,19 @@ describe('useTextBasedQueryLanguage', () => { }); }); - test('changing a text based query with an index pattern that not corresponds to a dataview should return results', async () => { + test('changing an ES|QL query with an index pattern that not corresponds to a dataview should return results', async () => { const props = getHookProps(query, discoverServiceMock.dataViews); const { stateContainer, replaceUrlState } = props; const documents$ = stateContainer.dataState.data$.documents$; props.stateContainer.actions.setDataView(dataViewMock); - renderHook(() => useTextBasedQueryLanguage(props), { wrapper: getHookContext(stateContainer) }); + renderHook(() => useEsqlMode(props), { wrapper: getHookContext(stateContainer) }); documents$.next(msgComplete); await waitFor(() => expect(replaceUrlState).toHaveBeenCalledTimes(0)); replaceUrlState.mockReset(); documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.PARTIAL, result: [ { diff --git a/src/plugins/discover/public/application/main/hooks/use_text_based_query_language.ts b/src/plugins/discover/public/application/main/hooks/use_esql_mode.ts similarity index 90% rename from src/plugins/discover/public/application/main/hooks/use_text_based_query_language.ts rename to src/plugins/discover/public/application/main/hooks/use_esql_mode.ts index 063a5c21bfbe3..a907b1e796c87 100644 --- a/src/plugins/discover/public/application/main/hooks/use_text_based_query_language.ts +++ b/src/plugins/discover/public/application/main/hooks/use_esql_mode.ts @@ -5,6 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ + import { isEqual } from 'lodash'; import { isOfAggregateQueryType, getAggregateQueryMode } from '@kbn/es-query'; import { useCallback, useEffect, useRef } from 'react'; @@ -20,10 +21,10 @@ const MAX_NUM_OF_COLUMNS = 50; const TRANSFORMATIONAL_COMMANDS = ['stats', 'keep']; /** - * Hook to take care of text based query language state transformations when a new result is returned + * Hook to take care of ES|QL state transformations when a new result is returned * If necessary this is setting displayed columns and selected data view */ -export function useTextBasedQueryLanguage({ +export function useEsqlMode({ dataViews, stateContainer, }: { @@ -42,7 +43,7 @@ export function useTextBasedQueryLanguage({ const cleanup = useCallback(() => { if (prev.current.query) { - // cleanup when it's not a text based query lang + // cleanup when it's not an ES|QL query prev.current = { columns: [], query: '', @@ -54,7 +55,7 @@ export function useTextBasedQueryLanguage({ const subscription = stateContainer.dataState.data$.documents$ .pipe( switchMap(async (next) => { - const { query, recordRawType } = next; + const { query } = next; if (!query || next.fetchStatus === FetchStatus.ERROR) { return; } @@ -66,7 +67,7 @@ export function useTextBasedQueryLanguage({ }; const { viewMode } = stateContainer.appState.getState(); let nextColumns: string[] = []; - const isTextBasedQueryLang = recordRawType === 'plain' && isOfAggregateQueryType(query); + const isEsqlQuery = isOfAggregateQueryType(query); const hasResults = Boolean(next.result?.length); let queryHasTransformationalCommands = false; if ('esql' in query) { @@ -78,7 +79,7 @@ export function useTextBasedQueryLanguage({ }); } - if (isTextBasedQueryLang) { + if (isEsqlQuery) { const language = getAggregateQueryMode(query); if (next.fetchStatus !== FetchStatus.PARTIAL) { return; @@ -100,8 +101,7 @@ export function useTextBasedQueryLanguage({ } const addColumnsToState = !isEqual(nextColumns, prev.current.columns); const queryChanged = query[language] !== prev.current.query; - const changeViewMode = - viewMode !== getValidViewMode({ viewMode, isTextBasedQueryMode: true }); + const changeViewMode = viewMode !== getValidViewMode({ viewMode, isEsqlMode: true }); if (!queryChanged || (!addColumnsToState && !changeViewMode)) { sendComplete(); return; diff --git a/src/plugins/discover/public/application/main/hooks/use_behavior_subject.ts b/src/plugins/discover/public/application/main/hooks/use_is_esql_mode.ts similarity index 50% rename from src/plugins/discover/public/application/main/hooks/use_behavior_subject.ts rename to src/plugins/discover/public/application/main/hooks/use_is_esql_mode.ts index 5be6bd3266c7b..49f83ff1bf9c5 100644 --- a/src/plugins/discover/public/application/main/hooks/use_behavior_subject.ts +++ b/src/plugins/discover/public/application/main/hooks/use_is_esql_mode.ts @@ -5,15 +5,11 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { useRef } from 'react'; -import { BehaviorSubject } from 'rxjs'; -export function useBehaviorSubject(props: T): BehaviorSubject { - const ref = useRef | null>(null); +import { DataSourceType, isDataSourceType } from '../../../../common/data_sources'; +import { useAppStateSelector } from '../state_management/discover_app_state_container'; - if (ref.current === null) { - ref.current = new BehaviorSubject(props); - } - - return ref.current; -} +export const useIsEsqlMode = () => { + const dataSource = useAppStateSelector((state) => state.dataSource); + return isDataSourceType(dataSource, DataSourceType.Esql); +}; diff --git a/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.test.ts b/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.test.ts index 6faf1c29257d9..4097831464669 100644 --- a/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.test.ts +++ b/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.test.ts @@ -18,11 +18,7 @@ import { } from './use_saved_search_messages'; import { FetchStatus } from '../../types'; import { BehaviorSubject } from 'rxjs'; -import { - DataDocumentsMsg, - DataMainMsg, - RecordRawType, -} from '../state_management/discover_data_state_container'; +import { DataDocumentsMsg, DataMainMsg } from '../state_management/discover_data_state_container'; import { filter } from 'rxjs'; import { dataViewMock, esHitsMockWithSort } from '@kbn/discover-utils/src/__mocks__'; import { buildDataTableRecord } from '@kbn/discover-utils'; @@ -69,13 +65,11 @@ describe('test useSavedSearch message generators', () => { main$.subscribe((value) => { if (value.fetchStatus !== FetchStatus.COMPLETE) { expect(value.fetchStatus).toBe(FetchStatus.LOADING); - expect(value.recordRawType).toBe(RecordRawType.DOCUMENT); done(); } }); sendLoadingMsg(main$, { foundDocuments: true, - recordRawType: RecordRawType.DOCUMENT, }); }); test('sendLoadingMoreMsg', (done) => { diff --git a/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.ts b/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.ts index 9374045d4b0d5..66d022df5f3ff 100644 --- a/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.ts +++ b/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.ts @@ -17,7 +17,6 @@ import type { DataTotalHits$, SavedSearchData, } from '../state_management/discover_data_state_container'; -import { RecordRawType } from '../state_management/discover_data_state_container'; /** * Sends COMPLETE message to the main$ observable with the information @@ -37,13 +36,7 @@ export function sendCompleteMsg(main$: DataMain$, foundDocuments = true) { if (main$.getValue().fetchStatus === FetchStatus.COMPLETE) { return; } - const recordRawType = main$.getValue().recordRawType; - main$.next({ - fetchStatus: FetchStatus.COMPLETE, - foundDocuments, - error: undefined, - recordRawType, - }); + main$.next({ fetchStatus: FetchStatus.COMPLETE, foundDocuments, error: undefined }); } /** @@ -51,11 +44,7 @@ export function sendCompleteMsg(main$: DataMain$, foundDocuments = true) { */ export function sendPartialMsg(main$: DataMain$) { if (main$.getValue().fetchStatus === FetchStatus.LOADING) { - const recordRawType = main$.getValue().recordRawType; - main$.next({ - fetchStatus: FetchStatus.PARTIAL, - recordRawType, - }); + main$.next({ fetchStatus: FetchStatus.PARTIAL }); } } @@ -64,13 +53,10 @@ export function sendPartialMsg(main$: DataMain$) { */ export function sendLoadingMsg( data$: BehaviorSubject, - props: Omit + props?: Omit ) { if (data$.getValue().fetchStatus !== FetchStatus.LOADING) { - data$.next({ - ...props, - fetchStatus: FetchStatus.LOADING, - } as T); + data$.next({ ...props, fetchStatus: FetchStatus.LOADING } as T); } } @@ -79,10 +65,7 @@ export function sendLoadingMsg( */ export function sendLoadingMoreMsg(documents$: DataDocuments$) { if (documents$.getValue().fetchStatus !== FetchStatus.LOADING_MORE) { - documents$.next({ - ...documents$.getValue(), - fetchStatus: FetchStatus.LOADING_MORE, - }); + documents$.next({ ...documents$.getValue(), fetchStatus: FetchStatus.LOADING_MORE }); } } @@ -116,38 +99,17 @@ export function sendLoadingMoreFinishedMsg( * Send ERROR message */ export function sendErrorMsg(data$: DataMain$ | DataDocuments$ | DataTotalHits$, error?: Error) { - const recordRawType = data$.getValue().recordRawType; - data$.next({ - fetchStatus: FetchStatus.ERROR, - error, - recordRawType, - }); + data$.next({ fetchStatus: FetchStatus.ERROR, error }); } /** * Sends a RESET message to all data subjects * Needed when data view is switched or a new runtime field is added */ -export function sendResetMsg( - data: SavedSearchData, - initialFetchStatus: FetchStatus, - recordRawType: RecordRawType -) { - data.main$.next({ - fetchStatus: initialFetchStatus, - foundDocuments: undefined, - recordRawType, - }); - data.documents$.next({ - fetchStatus: initialFetchStatus, - result: [], - recordRawType, - }); - data.totalHits$.next({ - fetchStatus: initialFetchStatus, - result: undefined, - recordRawType, - }); +export function sendResetMsg(data: SavedSearchData, initialFetchStatus: FetchStatus) { + data.main$.next({ fetchStatus: initialFetchStatus, foundDocuments: undefined }); + data.documents$.next({ fetchStatus: initialFetchStatus, result: [] }); + data.totalHits$.next({ fetchStatus: initialFetchStatus, result: undefined }); } /** diff --git a/src/plugins/discover/public/application/main/state_management/discover_data_state_container.test.ts b/src/plugins/discover/public/application/main/state_management/discover_data_state_container.test.ts index ac022d5875359..67586670c01c4 100644 --- a/src/plugins/discover/public/application/main/state_management/discover_data_state_container.test.ts +++ b/src/plugins/discover/public/application/main/state_management/discover_data_state_container.test.ts @@ -10,9 +10,8 @@ import { waitFor } from '@testing-library/react'; import { buildDataTableRecord } from '@kbn/discover-utils'; import { dataViewMock, esHitsMockWithSort } from '@kbn/discover-utils/src/__mocks__'; import { discoverServiceMock } from '../../../__mocks__/services'; -import { savedSearchMockWithESQL } from '../../../__mocks__/saved_search'; import { FetchStatus } from '../../types'; -import { DataDocuments$, RecordRawType } from './discover_data_state_container'; +import { DataDocuments$ } from './discover_data_state_container'; import { getDiscoverStateMock } from '../../../__mocks__/discover_state.mock'; import { fetchDocuments } from '../data_fetching/fetch_documents'; @@ -92,23 +91,13 @@ describe('test getDataStateContainer', () => { await waitFor(() => { expect(dataState.data$.main$.value.fetchStatus).toBe(FetchStatus.COMPLETE); }); - dataState.reset(stateContainer.savedSearchState.getState()); + dataState.reset(); await waitFor(() => { expect(dataState.data$.main$.value.fetchStatus).toBe(FetchStatus.LOADING); }); unsubscribe(); }); - test('useSavedSearch returns plain record raw type', async () => { - const stateContainer = getDiscoverStateMock({ - savedSearch: savedSearchMockWithESQL, - }); - stateContainer.savedSearchState.load = jest.fn().mockResolvedValue(savedSearchMockWithESQL); - await stateContainer.actions.loadSavedSearch({ savedSearchId: savedSearchMockWithESQL.id }); - - expect(stateContainer.dataState.data$.main$.getValue().recordRawType).toBe(RecordRawType.PLAIN); - }); - test('refetch$ accepts "fetch_more" signal', (done) => { const records = esHitsMockWithSort.map((hit) => buildDataTableRecord(hit, dataViewMock)); const initialRecords = [records[0], records[1]]; diff --git a/src/plugins/discover/public/application/main/state_management/discover_data_state_container.ts b/src/plugins/discover/public/application/main/state_management/discover_data_state_container.ts index 203bd87ec84f1..71ad2ed87e79b 100644 --- a/src/plugins/discover/public/application/main/state_management/discover_data_state_container.ts +++ b/src/plugins/discover/public/application/main/state_management/discover_data_state_container.ts @@ -5,21 +5,20 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ + import { BehaviorSubject, filter, map, mergeMap, Observable, share, Subject, tap } from 'rxjs'; import type { AutoRefreshDoneFn } from '@kbn/data-plugin/public'; import type { DatatableColumn } from '@kbn/expressions-plugin/common'; import { RequestAdapter } from '@kbn/inspector-plugin/common'; import { SavedSearch } from '@kbn/saved-search-plugin/public'; -import { AggregateQuery, Query } from '@kbn/es-query'; +import { AggregateQuery, isOfAggregateQueryType, Query } from '@kbn/es-query'; import type { SearchResponse } from '@elastic/elasticsearch/lib/api/types'; import { DataView } from '@kbn/data-views-plugin/common'; import { reportPerformanceMetricEvent } from '@kbn/ebt-tools'; import type { SearchResponseWarning } from '@kbn/search-response-warnings'; import type { DataTableRecord } from '@kbn/discover-utils/types'; import { SEARCH_FIELDS_FROM_SOURCE, SEARCH_ON_PAGE_LOAD_SETTING } from '@kbn/discover-utils'; -import { getDataViewByTextBasedQueryLang } from './utils/get_data_view_by_text_based_query_lang'; -import { isTextBasedQuery } from '../utils/is_text_based_query'; -import { getRawRecordType } from '../utils/get_raw_record_type'; +import { getEsqlDataView } from './utils/get_esql_data_view'; import { DiscoverAppState } from './discover_app_state_container'; import { DiscoverServices } from '../../../build_services'; import { DiscoverSearchSessionManager } from './discover_search_session'; @@ -51,23 +50,11 @@ export type DataFetch$ = Observable<{ export type DataRefetch$ = Subject; -export enum RecordRawType { - /** - * Documents returned Elasticsearch, nested structure - */ - DOCUMENT = 'document', - /** - * Data returned e.g. ES|QL queries, flat structure - * */ - PLAIN = 'plain', -} - export type DataRefetchMsg = 'reset' | 'fetch_more' | undefined; export interface DataMsg { fetchStatus: FetchStatus; error?: Error; - recordRawType?: RecordRawType; query?: AggregateQuery | Query | undefined; } @@ -77,8 +64,8 @@ export interface DataMainMsg extends DataMsg { export interface DataDocumentsMsg extends DataMsg { result?: DataTableRecord[]; - textBasedQueryColumns?: DatatableColumn[]; // columns from text-based request - textBasedHeaderWarning?: string; + esqlQueryColumns?: DatatableColumn[]; // columns from ES|QL request + esqlHeaderWarning?: string; interceptedWarnings?: SearchResponseWarning[]; // warnings (like shard failures) } @@ -122,7 +109,7 @@ export interface DiscoverDataStateContainer { /** * resetting all data observable to initial state */ - reset: (savedSearch: SavedSearch) => void; + reset: () => void; /** * cancels the running queries @@ -168,8 +155,7 @@ export function getDataStateContainer({ const { data, uiSettings, toastNotifications } = services; const { timefilter } = data.query.timefilter; const inspectorAdapters = { requests: new RequestAdapter() }; - const appState = getAppState(); - const recordRawType = getRawRecordType(appState.query); + /** * The observable to trigger data fetching in UI * By refetch$.next('reset') rows and fieldcounts are reset to allow e.g. editing of runtime fields @@ -189,7 +175,7 @@ export function getDataStateContainer({ * The observables the UI (aka React component) subscribes to get notified about * the changes in the data fetching process (high level: fetching started, data was received) */ - const initialState = { fetchStatus: getInitialFetchStatus(), recordRawType }; + const initialState = { fetchStatus: getInitialFetchStatus() }; const dataSubjects: SavedSearchData = { main$: new BehaviorSubject(initialState), documents$: new BehaviorSubject(initialState), @@ -300,8 +286,8 @@ export function getDataStateContainer({ const query = getAppState().query; const currentDataView = getSavedSearch().searchSource.getField('index'); - if (isTextBasedQuery(query)) { - const nextDataView = await getDataViewByTextBasedQueryLang(query, currentDataView, services); + if (isOfAggregateQueryType(query)) { + const nextDataView = await getEsqlDataView(query, currentDataView, services); if (nextDataView !== currentDataView) { setDataView(nextDataView); } @@ -320,9 +306,8 @@ export function getDataStateContainer({ return refetch$; }; - const reset = (savedSearch: SavedSearch) => { - const recordType = getRawRecordType(savedSearch.searchSource.getField('query')); - sendResetMsg(dataSubjects, getInitialFetchStatus(), recordType); + const reset = () => { + sendResetMsg(dataSubjects, getInitialFetchStatus()); }; const cancel = () => { diff --git a/src/plugins/discover/public/application/main/state_management/utils/build_state_subscribe.ts b/src/plugins/discover/public/application/main/state_management/utils/build_state_subscribe.ts index fbd324a1a417c..5c71311377595 100644 --- a/src/plugins/discover/public/application/main/state_management/utils/build_state_subscribe.ts +++ b/src/plugins/discover/public/application/main/state_management/utils/build_state_subscribe.ts @@ -5,6 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ + import { isEqual } from 'lodash'; import type { DiscoverInternalStateContainer } from '../discover_internal_state_container'; import type { DiscoverServices } from '../../../../build_services'; @@ -17,7 +18,6 @@ import { isEqualState, } from '../discover_app_state_container'; import { addLog } from '../../../../utils/add_log'; -import { isTextBasedQuery } from '../../utils/is_text_based_query'; import { FetchStatus } from '../../../types'; import { loadAndResolveDataView } from './resolve_data_view'; import { @@ -52,11 +52,11 @@ export const buildStateSubscribe = const nextQuery = nextState.query; const savedSearch = savedSearchState.getState(); const prevQuery = savedSearch.searchSource.getField('query'); - const isTextBasedQueryLang = isTextBasedQuery(nextQuery); + const isEsqlMode = isDataSourceType(nextState.dataSource, DataSourceType.Esql); const queryChanged = !isEqual(nextQuery, prevQuery) || !isEqual(nextQuery, prevState.query); if ( - isTextBasedQueryLang && + isEsqlMode && isEqualState(prevState, nextState, ['dataSource', 'viewMode']) && !queryChanged ) { @@ -73,11 +73,11 @@ export const buildStateSubscribe = addLog('[appstate] subscribe triggered', nextState); - if (isTextBasedQueryLang) { - const isTextBasedQueryLangPrev = isTextBasedQuery(prevQuery); - if (!isTextBasedQueryLangPrev) { + if (isEsqlMode) { + const isEsqlModePrev = isDataSourceType(prevState.dataSource, DataSourceType.Esql); + if (!isEsqlModePrev) { savedSearchState.update({ nextState }); - dataState.reset(savedSearch); + dataState.reset(); } } @@ -85,11 +85,11 @@ export const buildStateSubscribe = // Cast to boolean to avoid false positives when comparing // undefined and false, which would trigger a refetch const chartDisplayChanged = Boolean(nextState.hideChart) !== Boolean(hideChart); - const chartIntervalChanged = nextState.interval !== interval && !isTextBasedQueryLang; + const chartIntervalChanged = nextState.interval !== interval && !isEsqlMode; const breakdownFieldChanged = nextState.breakdownField !== breakdownField; const sampleSizeChanged = nextState.sampleSize !== sampleSize; - const docTableSortChanged = !isEqual(nextState.sort, sort) && !isTextBasedQueryLang; - const dataSourceChanged = !isEqual(nextState.dataSource, dataSource) && !isTextBasedQueryLang; + const docTableSortChanged = !isEqual(nextState.sort, sort) && !isEsqlMode; + const dataSourceChanged = !isEqual(nextState.dataSource, dataSource) && !isEsqlMode; let savedSearchDataView; @@ -100,7 +100,7 @@ export const buildStateSubscribe = : undefined; const { dataView: nextDataView, fallback } = await loadAndResolveDataView( - { id: dataViewId, savedSearch, isTextBasedQuery: isTextBasedQueryLang }, + { id: dataViewId, savedSearch, isEsqlMode }, { internalStateContainer: internalState, services } ); @@ -120,7 +120,7 @@ export const buildStateSubscribe = } savedSearch.searchSource.setField('index', nextDataView); - dataState.reset(savedSearch); + dataState.reset(); setDataView(nextDataView); savedSearchDataView = nextDataView; } diff --git a/src/plugins/discover/public/application/main/state_management/utils/cleanup_url_state.ts b/src/plugins/discover/public/application/main/state_management/utils/cleanup_url_state.ts index ebf5f8dc90bd3..861c85ee20e65 100644 --- a/src/plugins/discover/public/application/main/state_management/utils/cleanup_url_state.ts +++ b/src/plugins/discover/public/application/main/state_management/utils/cleanup_url_state.ts @@ -5,6 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ + import { isOfAggregateQueryType } from '@kbn/es-query'; import type { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; import { DiscoverAppState, AppStateUrl } from '../discover_app_state_container'; @@ -20,12 +21,11 @@ export function cleanupUrlState( appStateFromUrl: AppStateUrl, uiSettings: IUiSettingsClient ): DiscoverAppState { - if ( - appStateFromUrl.query && - !isOfAggregateQueryType(appStateFromUrl.query) && - !appStateFromUrl.query.language - ) { - appStateFromUrl.query = migrateLegacyQuery(appStateFromUrl.query); + const query = appStateFromUrl.query; + const isEsqlQuery = isOfAggregateQueryType(query); + + if (!isEsqlQuery && query && !query.language) { + appStateFromUrl.query = migrateLegacyQuery(query); } if (typeof appStateFromUrl.sort?.[0] === 'string') { @@ -53,7 +53,7 @@ export function cleanupUrlState( if ( appStateFromUrl.sampleSize && - (isOfAggregateQueryType(appStateFromUrl.query) || // not supported yet for ES|QL + (isEsqlQuery || // not supported yet for ES|QL !( typeof appStateFromUrl.sampleSize === 'number' && appStateFromUrl.sampleSize > 0 && @@ -67,7 +67,7 @@ export function cleanupUrlState( if (appStateFromUrl.index) { if (!appStateFromUrl.dataSource) { // Convert the provided index to a data source - appStateFromUrl.dataSource = isOfAggregateQueryType(appStateFromUrl.query) + appStateFromUrl.dataSource = isEsqlQuery ? createEsqlDataSource() : createDataViewDataSource({ dataViewId: appStateFromUrl.index }); } diff --git a/src/plugins/discover/public/application/main/state_management/utils/get_data_view_by_text_based_query_lang.test.ts b/src/plugins/discover/public/application/main/state_management/utils/get_esql_data_view.test.ts similarity index 81% rename from src/plugins/discover/public/application/main/state_management/utils/get_data_view_by_text_based_query_lang.test.ts rename to src/plugins/discover/public/application/main/state_management/utils/get_esql_data_view.test.ts index b8053c47fd174..81189661b543b 100644 --- a/src/plugins/discover/public/application/main/state_management/utils/get_data_view_by_text_based_query_lang.test.ts +++ b/src/plugins/discover/public/application/main/state_management/utils/get_esql_data_view.test.ts @@ -6,12 +6,12 @@ * Side Public License, v 1. */ -import { getDataViewByTextBasedQueryLang } from './get_data_view_by_text_based_query_lang'; +import { getEsqlDataView } from './get_esql_data_view'; import { dataViewAdHoc } from '../../../../__mocks__/data_view_complex'; import { dataViewMock } from '@kbn/discover-utils/src/__mocks__'; import { discoverServiceMock } from '../../../../__mocks__/services'; -describe('getDataViewByTextBasedQueryLang', () => { +describe('getEsqlDataView', () => { discoverServiceMock.dataViews.create = jest.fn().mockReturnValue({ ...dataViewMock, isPersisted: () => false, @@ -21,13 +21,13 @@ describe('getDataViewByTextBasedQueryLang', () => { const services = discoverServiceMock; it('returns the current dataview if is adhoc and query has not changed', async () => { const query = { esql: 'from data-view-ad-hoc-title' }; - const dataView = await getDataViewByTextBasedQueryLang(query, dataViewAdHoc, services); + const dataView = await getEsqlDataView(query, dataViewAdHoc, services); expect(dataView).toStrictEqual(dataViewAdHoc); }); it('creates an adhoc dataview if the current dataview is persistent and query has not changed', async () => { const query = { esql: 'from the-data-view-title' }; - const dataView = await getDataViewByTextBasedQueryLang(query, dataViewMock, services); + const dataView = await getEsqlDataView(query, dataViewMock, services); expect(dataView.isPersisted()).toEqual(false); expect(dataView.timeFieldName).toBe('@timestamp'); }); @@ -41,7 +41,7 @@ describe('getDataViewByTextBasedQueryLang', () => { timeFieldName: undefined, }); const query = { esql: 'from the-data-view-title' }; - const dataView = await getDataViewByTextBasedQueryLang(query, dataViewAdHoc, services); + const dataView = await getEsqlDataView(query, dataViewAdHoc, services); expect(dataView.isPersisted()).toEqual(false); expect(dataView.timeFieldName).toBeUndefined(); }); @@ -55,7 +55,7 @@ describe('getDataViewByTextBasedQueryLang', () => { timeFieldName: undefined, }); const query = { esql: 'ROW x = "ES|QL is awesome"' }; - const dataView = await getDataViewByTextBasedQueryLang(query, dataViewAdHoc, services); + const dataView = await getEsqlDataView(query, dataViewAdHoc, services); expect(dataView.isPersisted()).toEqual(false); expect(dataView.name).toEqual(dataViewAdHoc.name); expect(dataView.timeFieldName).toBeUndefined(); diff --git a/src/plugins/discover/public/application/main/state_management/utils/get_data_view_by_text_based_query_lang.ts b/src/plugins/discover/public/application/main/state_management/utils/get_esql_data_view.ts similarity index 96% rename from src/plugins/discover/public/application/main/state_management/utils/get_data_view_by_text_based_query_lang.ts rename to src/plugins/discover/public/application/main/state_management/utils/get_esql_data_view.ts index db964c073a253..c4dd4d7e163b8 100644 --- a/src/plugins/discover/public/application/main/state_management/utils/get_data_view_by_text_based_query_lang.ts +++ b/src/plugins/discover/public/application/main/state_management/utils/get_esql_data_view.ts @@ -10,7 +10,7 @@ import { getESQLAdHocDataview, getIndexPatternFromESQLQuery } from '@kbn/esql-ut import { DataView } from '@kbn/data-views-plugin/common'; import { DiscoverServices } from '../../../../build_services'; -export async function getDataViewByTextBasedQueryLang( +export async function getEsqlDataView( query: AggregateQuery, currentDataView: DataView | undefined, services: DiscoverServices diff --git a/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.test.ts b/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.test.ts index 5e070ef099cde..ac85a24f91f34 100644 --- a/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.test.ts +++ b/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.test.ts @@ -98,24 +98,24 @@ describe('getStateDefaults', () => { }); expect(actualForUndefinedViewMode.viewMode).toBeUndefined(); - const actualForTextBasedWithInvalidViewMode = getStateDefaults({ + const actualForEsqlWithInvalidViewMode = getStateDefaults({ services: discoverServiceMock, savedSearch: { ...savedSearchMockWithESQL, viewMode: VIEW_MODE.AGGREGATED_LEVEL, }, }); - expect(actualForTextBasedWithInvalidViewMode.viewMode).toBe(VIEW_MODE.DOCUMENT_LEVEL); + expect(actualForEsqlWithInvalidViewMode.viewMode).toBe(VIEW_MODE.DOCUMENT_LEVEL); - const actualForTextBasedWithValidViewMode = getStateDefaults({ + const actualForEsqlWithValidViewMode = getStateDefaults({ services: discoverServiceMock, savedSearch: { ...savedSearchMockWithESQL, viewMode: VIEW_MODE.DOCUMENT_LEVEL, }, }); - expect(actualForTextBasedWithValidViewMode.viewMode).toBe(VIEW_MODE.DOCUMENT_LEVEL); - expect(actualForTextBasedWithValidViewMode.dataSource).toEqual(createEsqlDataSource()); + expect(actualForEsqlWithValidViewMode.viewMode).toBe(VIEW_MODE.DOCUMENT_LEVEL); + expect(actualForEsqlWithValidViewMode.dataSource).toEqual(createEsqlDataSource()); const actualForWithValidViewMode = getStateDefaults({ services: discoverServiceMock, @@ -133,11 +133,11 @@ describe('getStateDefaults', () => { }); test('should return expected dataSource', () => { - const actualForTextBased = getStateDefaults({ + const actualForEsql = getStateDefaults({ services: discoverServiceMock, savedSearch: savedSearchMockWithESQL, }); - expect(actualForTextBased.dataSource).toMatchInlineSnapshot(` + expect(actualForEsql.dataSource).toMatchInlineSnapshot(` Object { "type": "esql", } diff --git a/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.ts b/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.ts index 7a1f2734aa7c8..7e44adf64dbc1 100644 --- a/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.ts +++ b/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.ts @@ -16,16 +16,12 @@ import { SEARCH_FIELDS_FROM_SOURCE, SORT_DEFAULT_ORDER_SETTING, } from '@kbn/discover-utils'; +import { isOfAggregateQueryType } from '@kbn/es-query'; import { DiscoverAppState } from '../discover_app_state_container'; import { DiscoverServices } from '../../../../build_services'; import { getDefaultSort, getSortArray } from '../../../../utils/sorting'; -import { isTextBasedQuery } from '../../utils/is_text_based_query'; import { getValidViewMode } from '../../utils/get_valid_view_mode'; -import { - createDataViewDataSource, - createEsqlDataSource, - DiscoverDataSource, -} from '../../../../../common/data_sources'; +import { createDataViewDataSource, createEsqlDataSource } from '../../../../../common/data_sources'; function getDefaultColumns(savedSearch: SavedSearch, uiSettings: IUiSettingsClient) { if (savedSearch.columns && savedSearch.columns.length > 0) { @@ -51,11 +47,11 @@ export function getStateDefaults({ const { data, uiSettings, storage } = services; const dataView = searchSource.getField('index'); const query = searchSource.getField('query') || data.query.queryString.getDefaultQuery(); - const isTextBasedQueryMode = isTextBasedQuery(query); - const sort = getSortArray(savedSearch.sort ?? [], dataView!, isTextBasedQueryMode); + const isEsqlQuery = isOfAggregateQueryType(query); + const sort = getSortArray(savedSearch.sort ?? [], dataView!, isEsqlQuery); const columns = getDefaultColumns(savedSearch, uiSettings); const chartHidden = getChartHidden(storage, 'discover'); - const dataSource: DiscoverDataSource | undefined = isTextBasedQueryMode + const dataSource = isEsqlQuery ? createEsqlDataSource() : dataView?.id ? createDataViewDataSource({ dataViewId: dataView.id }) @@ -68,7 +64,7 @@ export function getStateDefaults({ dataView, uiSettings.get(SORT_DEFAULT_ORDER_SETTING, 'desc'), uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING, false), - isTextBasedQueryMode + isEsqlQuery ) : sort, columns, @@ -102,7 +98,7 @@ export function getStateDefaults({ if (savedSearch.viewMode) { defaultState.viewMode = getValidViewMode({ viewMode: savedSearch.viewMode, - isTextBasedQueryMode, + isEsqlMode: isEsqlQuery, }); } if (savedSearch.hideAggregatedPreview) { diff --git a/src/plugins/discover/public/application/main/state_management/utils/get_switch_data_view_app_state.ts b/src/plugins/discover/public/application/main/state_management/utils/get_switch_data_view_app_state.ts index c06cb3b67f235..0960471c4a6dc 100644 --- a/src/plugins/discover/public/application/main/state_management/utils/get_switch_data_view_app_state.ts +++ b/src/plugins/discover/public/application/main/state_management/utils/get_switch_data_view_app_state.ts @@ -39,9 +39,9 @@ export function getDataViewAppState( ); } - const isTextBasedQueryMode = isOfAggregateQueryType(query); + const isEsqlQuery = isOfAggregateQueryType(query); - if (isTextBasedQueryMode) { + if (isEsqlQuery) { columns = []; } @@ -49,7 +49,7 @@ export function getDataViewAppState( // filter out sorting by timeField in case it is set. data views without timeField don't // prepend this field in the table, so in legacy grid you would need to add this column to // remove sorting - let nextSort = getSortArray(currentSort, nextDataView, isTextBasedQueryMode).filter((value) => { + let nextSort = getSortArray(currentSort, nextDataView, isEsqlQuery).filter((value) => { return nextDataView.timeFieldName || value[0] !== currentDataView.timeFieldName; }); diff --git a/src/plugins/discover/public/application/main/state_management/utils/load_saved_search.ts b/src/plugins/discover/public/application/main/state_management/utils/load_saved_search.ts index 0997f0f58b0aa..6a6d55535f47a 100644 --- a/src/plugins/discover/public/application/main/state_management/utils/load_saved_search.ts +++ b/src/plugins/discover/public/application/main/state_management/utils/load_saved_search.ts @@ -5,10 +5,11 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ + import type { SavedSearch } from '@kbn/saved-search-plugin/public'; import { cloneDeep, isEqual } from 'lodash'; -import { getDataViewByTextBasedQueryLang } from './get_data_view_by_text_based_query_lang'; -import { isTextBasedQuery } from '../../utils/is_text_based_query'; +import { isOfAggregateQueryType } from '@kbn/es-query'; +import { getEsqlDataView } from './get_esql_data_view'; import { loadAndResolveDataView } from './resolve_data_view'; import { DiscoverInternalStateContainer } from '../discover_internal_state_container'; import { DiscoverDataStateContainer } from '../discover_data_state_container'; @@ -160,7 +161,7 @@ function updateBySavedSearch(savedSearch: SavedSearch, deps: LoadSavedSearchDeps } // Finally notify dataStateContainer, data.query and filterManager about new derived state - dataStateContainer.reset(savedSearch); + dataStateContainer.reset(); // set data service filters const filters = savedSearch.searchSource.getField('filter'); if (Array.isArray(filters) && filters.length) { @@ -204,14 +205,14 @@ const getStateDataView = async ( } ) => { const { dataView, dataViewSpec } = params; - const isTextBased = isTextBasedQuery(query); + const isEsqlQuery = isOfAggregateQueryType(query); if (dataView) { return dataView; } - if (isTextBased) { - return await getDataViewByTextBasedQueryLang(query, dataView, services); + if (isEsqlQuery) { + return await getEsqlDataView(query, dataView, services); } const result = await loadAndResolveDataView( @@ -219,7 +220,7 @@ const getStateDataView = async ( id: dataViewId, dataViewSpec, savedSearch, - isTextBasedQuery: isTextBased, + isEsqlMode: isEsqlQuery, }, { services, internalStateContainer } ); diff --git a/src/plugins/discover/public/application/main/state_management/utils/resolve_data_view.ts b/src/plugins/discover/public/application/main/state_management/utils/resolve_data_view.ts index 031193c93cba5..55e18899a4e33 100644 --- a/src/plugins/discover/public/application/main/state_management/utils/resolve_data_view.ts +++ b/src/plugins/discover/public/application/main/state_management/utils/resolve_data_view.ts @@ -115,7 +115,7 @@ export function resolveDataView( ip: DataViewData, savedSearch: SavedSearch | undefined, toastNotifications: ToastsStart, - isTextBasedQuery?: boolean + isEsqlMode?: boolean ) { const { loaded: loadedDataView, stateVal, stateValFound } = ip; @@ -126,8 +126,8 @@ export function resolveDataView( return ownDataView; } - // no warnings for text based mode - if (stateVal && !stateValFound && !Boolean(isTextBasedQuery)) { + // no warnings for ES|QL mode + if (stateVal && !stateValFound && !Boolean(isEsqlMode)) { const warningTitle = i18n.translate('discover.valueIsNotConfiguredDataViewIDWarningTitle', { defaultMessage: '{stateVal} is not a configured data view ID', values: { @@ -172,12 +172,12 @@ export const loadAndResolveDataView = async ( id, dataViewSpec, savedSearch, - isTextBasedQuery, + isEsqlMode, }: { id?: string; dataViewSpec?: DataViewSpec; savedSearch?: SavedSearch; - isTextBasedQuery?: boolean; + isEsqlMode?: boolean; }, { internalStateContainer, @@ -198,7 +198,7 @@ export const loadAndResolveDataView = async ( nextDataViewData, savedSearch, services.toastNotifications, - isTextBasedQuery + isEsqlMode ); return { fallback: !nextDataViewData.stateValFound, dataView: nextDataView }; }; diff --git a/src/plugins/discover/public/application/main/state_management/utils/update_saved_search.ts b/src/plugins/discover/public/application/main/state_management/utils/update_saved_search.ts index ad47bb021ea71..19a5cd6c7fb88 100644 --- a/src/plugins/discover/public/application/main/state_management/utils/update_saved_search.ts +++ b/src/plugins/discover/public/application/main/state_management/utils/update_saved_search.ts @@ -5,13 +5,14 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ + import type { SavedSearch, SortOrder } from '@kbn/saved-search-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/common'; import { cloneDeep } from 'lodash'; -import { isTextBasedQuery } from '../../utils/is_text_based_query'; import type { DiscoverAppState } from '../discover_app_state_container'; import type { DiscoverServices } from '../../../../build_services'; import type { DiscoverGlobalStateContainer } from '../discover_global_state_container'; +import { DataSourceType, isDataSourceType } from '../../../../../common/data_sources'; /** * Updates the saved search with a given data view & Appstate @@ -76,11 +77,11 @@ export function updateSavedSearch({ savedSearch.breakdownField = state.breakdownField || undefined; // `undefined` instead of an empty string savedSearch.hideAggregatedPreview = state.hideAggregatedPreview; - // add a flag here to identify text based language queries + // add a flag here to identify ES|QL queries // these should be filtered out from the visualize editor - const isTextBasedQueryResult = isTextBasedQuery(state.query); - if (savedSearch.isTextBasedQuery || isTextBasedQueryResult) { - savedSearch.isTextBasedQuery = isTextBasedQueryResult; + const isEsqlMode = isDataSourceType(state.dataSource, DataSourceType.Esql); + if (savedSearch.isTextBasedQuery || isEsqlMode) { + savedSearch.isTextBasedQuery = isEsqlMode; } } diff --git a/src/plugins/discover/public/application/main/utils/get_raw_record_type.test.ts b/src/plugins/discover/public/application/main/utils/get_raw_record_type.test.ts deleted file mode 100644 index 9626c2d1caad1..0000000000000 --- a/src/plugins/discover/public/application/main/utils/get_raw_record_type.test.ts +++ /dev/null @@ -1,23 +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 { RecordRawType } from '../state_management/discover_data_state_container'; -import { getRawRecordType } from './get_raw_record_type'; - -describe('getRawRecordType', () => { - it('returns empty string for Query type query', () => { - const mode = getRawRecordType({ query: '', language: 'lucene' }); - expect(mode).toEqual(RecordRawType.DOCUMENT); - }); - - it('returns esql for Query type query', () => { - const mode = getRawRecordType({ esql: 'from foo' }); - - expect(mode).toEqual(RecordRawType.PLAIN); - }); -}); diff --git a/src/plugins/discover/public/application/main/utils/get_raw_record_type.ts b/src/plugins/discover/public/application/main/utils/get_raw_record_type.ts deleted file mode 100644 index 3a96973adfbac..0000000000000 --- a/src/plugins/discover/public/application/main/utils/get_raw_record_type.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 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 { AggregateQuery, Query, isOfAggregateQueryType } from '@kbn/es-query'; -import { RecordRawType } from '../state_management/discover_data_state_container'; - -export function getRawRecordType(query?: Query | AggregateQuery) { - if (query && isOfAggregateQueryType(query)) { - return RecordRawType.PLAIN; - } - - return RecordRawType.DOCUMENT; -} diff --git a/src/plugins/discover/public/application/main/utils/get_valid_view_mode.test.ts b/src/plugins/discover/public/application/main/utils/get_valid_view_mode.test.ts index 33431ba09b7c5..51530926defbf 100644 --- a/src/plugins/discover/public/application/main/utils/get_valid_view_mode.test.ts +++ b/src/plugins/discover/public/application/main/utils/get_valid_view_mode.test.ts @@ -14,44 +14,44 @@ describe('getValidViewMode', () => { expect( getValidViewMode({ viewMode: undefined, - isTextBasedQueryMode: false, + isEsqlMode: false, }) ).toBeUndefined(); expect( getValidViewMode({ viewMode: VIEW_MODE.DOCUMENT_LEVEL, - isTextBasedQueryMode: false, + isEsqlMode: false, }) ).toBe(VIEW_MODE.DOCUMENT_LEVEL); expect( getValidViewMode({ viewMode: VIEW_MODE.AGGREGATED_LEVEL, - isTextBasedQueryMode: false, + isEsqlMode: false, }) ).toBe(VIEW_MODE.AGGREGATED_LEVEL); }); - test('should work correctly for text-based mode', () => { + test('should work correctly for ES|QL mode', () => { expect( getValidViewMode({ viewMode: undefined, - isTextBasedQueryMode: true, + isEsqlMode: true, }) ).toBeUndefined(); expect( getValidViewMode({ viewMode: VIEW_MODE.DOCUMENT_LEVEL, - isTextBasedQueryMode: true, + isEsqlMode: true, }) ).toBe(VIEW_MODE.DOCUMENT_LEVEL); expect( getValidViewMode({ viewMode: VIEW_MODE.AGGREGATED_LEVEL, - isTextBasedQueryMode: true, + isEsqlMode: true, }) ).toBe(VIEW_MODE.DOCUMENT_LEVEL); }); diff --git a/src/plugins/discover/public/application/main/utils/get_valid_view_mode.ts b/src/plugins/discover/public/application/main/utils/get_valid_view_mode.ts index 4d6d660c28eef..eab9677f083b2 100644 --- a/src/plugins/discover/public/application/main/utils/get_valid_view_mode.ts +++ b/src/plugins/discover/public/application/main/utils/get_valid_view_mode.ts @@ -11,17 +11,17 @@ import { VIEW_MODE } from '@kbn/saved-search-plugin/public'; /** * Returns a valid view mode * @param viewMode - * @param isTextBasedQueryMode + * @param isEsqlMode */ export const getValidViewMode = ({ viewMode, - isTextBasedQueryMode, + isEsqlMode, }: { viewMode?: VIEW_MODE; - isTextBasedQueryMode: boolean; + isEsqlMode: boolean; }): VIEW_MODE | undefined => { - if (viewMode === VIEW_MODE.AGGREGATED_LEVEL && isTextBasedQueryMode) { - // only this mode is supported for text-based languages + if (viewMode === VIEW_MODE.AGGREGATED_LEVEL && isEsqlMode) { + // only this mode is supported for ES|QL languages return VIEW_MODE.DOCUMENT_LEVEL; } diff --git a/src/plugins/discover/public/application/main/utils/is_text_based_query.test.ts b/src/plugins/discover/public/application/main/utils/is_text_based_query.test.ts deleted file mode 100644 index 130007c55fc71..0000000000000 --- a/src/plugins/discover/public/application/main/utils/is_text_based_query.test.ts +++ /dev/null @@ -1,17 +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 { isTextBasedQuery } from './is_text_based_query'; - -describe('isTextBasedQuery', () => { - it('should work correctly', () => { - expect(isTextBasedQuery({ query: '', language: 'lucene' })).toEqual(false); - expect(isTextBasedQuery({ esql: 'from foo' })).toEqual(true); - expect(isTextBasedQuery()).toEqual(false); - }); -}); diff --git a/src/plugins/discover/public/application/main/utils/is_text_based_query.ts b/src/plugins/discover/public/application/main/utils/is_text_based_query.ts deleted file mode 100644 index d92433307aaa0..0000000000000 --- a/src/plugins/discover/public/application/main/utils/is_text_based_query.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 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 { AggregateQuery, Query } from '@kbn/es-query'; -import { RecordRawType } from '../state_management/discover_data_state_container'; -import { getRawRecordType } from './get_raw_record_type'; - -/** - * Checks if the query is of AggregateQuery type - * @param query - */ -export function isTextBasedQuery(query?: Query | AggregateQuery): query is AggregateQuery { - return getRawRecordType(query) === RecordRawType.PLAIN; -} diff --git a/src/plugins/discover/public/application/types.ts b/src/plugins/discover/public/application/types.ts index 2b5c85db1e73a..6fb55945861eb 100644 --- a/src/plugins/discover/public/application/types.ts +++ b/src/plugins/discover/public/application/types.ts @@ -21,8 +21,8 @@ export enum FetchStatus { export interface RecordsFetchResponse { records: DataTableRecord[]; - textBasedQueryColumns?: DatatableColumn[]; - textBasedHeaderWarning?: string; + esqlQueryColumns?: DatatableColumn[]; + esqlHeaderWarning?: string; interceptedWarnings?: SearchResponseWarning[]; } diff --git a/src/plugins/discover/public/components/discover_grid_flyout/discover_grid_flyout.test.tsx b/src/plugins/discover/public/components/discover_grid_flyout/discover_grid_flyout.test.tsx index 668f4090b3bdd..3907bc4999232 100644 --- a/src/plugins/discover/public/components/discover_grid_flyout/discover_grid_flyout.test.tsx +++ b/src/plugins/discover/public/components/discover_grid_flyout/discover_grid_flyout.test.tsx @@ -252,7 +252,7 @@ describe('Discover flyout', function () { expect(props.setExpandedDoc).not.toHaveBeenCalled(); }); - it('should not render single/surrounding views for text based', async () => { + it('should not render single/surrounding views for ES|QL', async () => { const { component } = await mountComponent({ query: { esql: 'FROM indexpattern' }, }); diff --git a/src/plugins/discover/public/components/discover_grid_flyout/discover_grid_flyout.tsx b/src/plugins/discover/public/components/discover_grid_flyout/discover_grid_flyout.tsx index 012f3674f48f4..5fc88c8442a1a 100644 --- a/src/plugins/discover/public/components/discover_grid_flyout/discover_grid_flyout.tsx +++ b/src/plugins/discover/public/components/discover_grid_flyout/discover_grid_flyout.tsx @@ -27,14 +27,13 @@ import { useEuiTheme, useIsWithinMinBreakpoint, } from '@elastic/eui'; -import type { Filter, Query, AggregateQuery } from '@kbn/es-query'; +import { Filter, Query, AggregateQuery, isOfAggregateQueryType } from '@kbn/es-query'; import type { DataTableRecord } from '@kbn/discover-utils/types'; import type { DocViewFilterFn } from '@kbn/unified-doc-viewer/types'; import type { DataTableColumnsMeta } from '@kbn/unified-data-table'; import { UnifiedDocViewer } from '@kbn/unified-doc-viewer-plugin/public'; import useLocalStorage from 'react-use/lib/useLocalStorage'; import { useDiscoverServices } from '../../hooks/use_discover_services'; -import { isTextBasedQuery } from '../../application/main/utils/is_text_based_query'; import { useFlyoutActions } from './use_flyout_actions'; import { useDiscoverCustomization } from '../../customizations'; import { DiscoverGridFlyoutActions } from './discover_grid_flyout_actions'; @@ -89,8 +88,7 @@ export function DiscoverGridFlyout({ const [flyoutWidth, setFlyoutWidth] = useLocalStorage(FLYOUT_WIDTH_KEY, defaultWidth); const minWidth = euiTheme.base * 24; const maxWidth = euiTheme.breakpoint.xl; - - const isPlainRecord = isTextBasedQuery(query); + const isEsqlQuery = isOfAggregateQueryType(query); // Get actual hit with updated highlighted searches const actualHit = useMemo(() => hits?.find(({ id }) => id === hit?.id) || hit, [hit, hits]); const pageCount = useMemo(() => (hits ? hits.length : 0), [hits]); @@ -173,7 +171,7 @@ export function DiscoverGridFlyout({ hit={actualHit} onAddColumn={addColumn} onRemoveColumn={removeColumn} - textBasedHits={isPlainRecord ? hits : undefined} + textBasedHits={isEsqlQuery ? hits : undefined} docViewsRegistry={flyoutCustomization?.docViewsRegistry} /> ), @@ -184,7 +182,7 @@ export function DiscoverGridFlyout({ columnsMeta, dataView, hits, - isPlainRecord, + isEsqlQuery, onFilter, removeColumn, flyoutCustomization?.docViewsRegistry, @@ -210,8 +208,8 @@ export function DiscoverGridFlyout({ renderDefaultContent() ); - const defaultFlyoutTitle = isPlainRecord - ? i18n.translate('discover.grid.tableRow.docViewerTextBasedDetailHeading', { + const defaultFlyoutTitle = isEsqlQuery + ? i18n.translate('discover.grid.tableRow.docViewerEsqlDetailHeading', { defaultMessage: 'Result', }) : i18n.translate('discover.grid.tableRow.docViewerDetailHeading', { @@ -272,7 +270,7 @@ export function DiscoverGridFlyout({ )} - {isPlainRecord || !flyoutActions.length ? null : ( + {isEsqlQuery || !flyoutActions.length ? null : ( <> diff --git a/src/plugins/discover/public/components/discover_tour/discover_tour.test.tsx b/src/plugins/discover/public/components/discover_tour/discover_tour.test.tsx index 4bdbeaa663818..5c58201bbf563 100644 --- a/src/plugins/discover/public/components/discover_tour/discover_tour.test.tsx +++ b/src/plugins/discover/public/components/discover_tour/discover_tour.test.tsx @@ -14,12 +14,16 @@ import { discoverServiceMock } from '../../__mocks__/services'; import { DiscoverTourProvider } from './discover_tour_provider'; import { useDiscoverTourContext } from './discover_tour_context'; import { DISCOVER_TOUR_STEP_ANCHORS } from './discover_tour_anchors'; +import { DiscoverMainProvider } from '../../application/main/state_management/discover_state_provider'; +import { getDiscoverStateMock } from '../../__mocks__/discover_state.mock'; describe('Discover tour', () => { const mountComponent = (innerContent: JSX.Element) => { return mountWithIntl( - {innerContent} + + {innerContent} + ); }; diff --git a/src/plugins/discover/public/components/discover_tour/discover_tour_provider.tsx b/src/plugins/discover/public/components/discover_tour/discover_tour_provider.tsx index 45a870fbc2806..71ed5b85e5ee3 100644 --- a/src/plugins/discover/public/components/discover_tour/discover_tour_provider.tsx +++ b/src/plugins/discover/public/components/discover_tour/discover_tour_provider.tsx @@ -29,6 +29,7 @@ import { PLUGIN_ID } from '../../../common'; import { useDiscoverServices } from '../../hooks/use_discover_services'; import { DiscoverTourContext, DiscoverTourContextProps } from './discover_tour_context'; import { DISCOVER_TOUR_STEP_ANCHORS } from './discover_tour_anchors'; +import { useIsEsqlMode } from '../../application/main/hooks/use_is_esql_mode'; const MAX_WIDTH = 350; @@ -198,14 +199,9 @@ const tourConfig: EuiTourState = { tourSubtitle: '', }; -export const DiscoverTourProvider = ({ - children, - isPlainRecord, -}: { - children: ReactElement; - isPlainRecord: boolean; -}) => { +export const DiscoverTourProvider = ({ children }: { children: ReactElement }) => { const services = useDiscoverServices(); + const isEsqlMode = useIsEsqlMode(); const prependToBasePath = services.core.http.basePath.prepend; const getAssetPath = useCallback( (imageName: string) => { @@ -215,13 +211,13 @@ export const DiscoverTourProvider = ({ ); const tourSteps = useMemo( () => - isPlainRecord + isEsqlMode ? prepareTourSteps( [ADD_FIELDS_STEP, ORDER_TABLE_COLUMNS_STEP, CHANGE_ROW_HEIGHT_STEP], getAssetPath ) : prepareTourSteps(tourStepDefinitions, getAssetPath), - [getAssetPath, isPlainRecord] + [getAssetPath, isEsqlMode] ); const [steps, actions, reducerState] = useEuiTour(tourSteps, tourConfig); const currentTourStep = reducerState.currentTourStep; diff --git a/src/plugins/discover/public/components/doc_table/components/table_row.test.tsx b/src/plugins/discover/public/components/doc_table/components/table_row.test.tsx index c4e53c265311d..ad8b1c5a2f547 100644 --- a/src/plugins/discover/public/components/doc_table/components/table_row.test.tsx +++ b/src/plugins/discover/public/components/doc_table/components/table_row.test.tsx @@ -125,10 +125,10 @@ describe('Doc table row component', () => { expect(findTestSubject(component, 'docTableRowDetailsTitle').exists()).toBeTruthy(); }); - it('should hide the single/surrounding views for text based languages', () => { + it('should hide the single/surrounding views for ES|QL mode', () => { const props = { ...defaultProps, - isPlainRecord: true, + isEsqlMode: true, }; const component = mountComponent(props); const toggleButton = findTestSubject(component, 'docTableExpandToggleColumn'); diff --git a/src/plugins/discover/public/components/doc_table/components/table_row.tsx b/src/plugins/discover/public/components/doc_table/components/table_row.tsx index 7c9d55e7049bf..18790ace55289 100644 --- a/src/plugins/discover/public/components/doc_table/components/table_row.tsx +++ b/src/plugins/discover/public/components/doc_table/components/table_row.tsx @@ -34,7 +34,7 @@ export interface TableRowProps { columns: string[]; filter?: DocViewFilterFn; filters?: Filter[]; - isPlainRecord?: boolean; + isEsqlMode?: boolean; savedSearchId?: string; row: DataTableRecord; rows: DataTableRecord[]; @@ -47,7 +47,7 @@ export interface TableRowProps { export const TableRow = ({ filters, - isPlainRecord, + isEsqlMode, columns, filter, savedSearchId, @@ -219,7 +219,7 @@ export const TableRow = ({ columns={columns} filters={filters} savedSearchId={savedSearchId} - isPlainRecord={isPlainRecord} + isEsqlMode={isEsqlMode} > )} diff --git a/src/plugins/discover/public/components/doc_table/components/table_row/__snapshots__/table_cell.test.tsx.snap b/src/plugins/discover/public/components/doc_table/components/table_row/__snapshots__/table_cell.test.tsx.snap index 555be566a5082..a6449fd265492 100644 --- a/src/plugins/discover/public/components/doc_table/components/table_row/__snapshots__/table_cell.test.tsx.snap +++ b/src/plugins/discover/public/components/doc_table/components/table_row/__snapshots__/table_cell.test.tsx.snap @@ -40,11 +40,13 @@ exports[`Doc table cell component renders a cell with filter buttons if it is fi isVisible={false} onBlur={[Function]} onFocus={[Function]} + onKeyDown={[Function]} onMouseOut={[Function]} onMouseOver={[Function]} > @@ -124,6 +126,7 @@ exports[`Doc table cell component renders a cell with filter buttons if it is fi /> @@ -164,11 +167,13 @@ exports[`Doc table cell component renders a cell with filter buttons if it is fi isVisible={false} onBlur={[Function]} onFocus={[Function]} + onKeyDown={[Function]} onMouseOut={[Function]} onMouseOver={[Function]} > @@ -248,6 +253,7 @@ exports[`Doc table cell component renders a cell with filter buttons if it is fi /> diff --git a/src/plugins/discover/public/components/doc_table/components/table_row_details.tsx b/src/plugins/discover/public/components/doc_table/components/table_row_details.tsx index 5a0aea52de3f4..cd203091e30f7 100644 --- a/src/plugins/discover/public/components/doc_table/components/table_row_details.tsx +++ b/src/plugins/discover/public/components/doc_table/components/table_row_details.tsx @@ -23,7 +23,7 @@ interface TableRowDetailsProps { dataView: DataView; filters?: Filter[]; savedSearchId?: string; - isPlainRecord?: boolean; + isEsqlMode?: boolean; } export const TableRowDetails = ({ @@ -36,7 +36,7 @@ export const TableRowDetails = ({ columns, filters, savedSearchId, - isPlainRecord, + isEsqlMode, }: TableRowDetailsProps) => { const { singleDocHref, contextViewHref, onOpenSingleDoc, onOpenContextView } = useNavigationProps( { @@ -60,13 +60,13 @@ export const TableRowDetails = ({

- {isPlainRecord && ( + {isEsqlMode && ( )} - {!isPlainRecord && ( + {!isEsqlMode && ( - {!isPlainRecord && ( + {!isEsqlMode && ( diff --git a/src/plugins/discover/public/components/doc_table/create_doc_table_embeddable.tsx b/src/plugins/discover/public/components/doc_table/create_doc_table_embeddable.tsx index 491bdd1166ec7..54622ac546fa3 100644 --- a/src/plugins/discover/public/components/doc_table/create_doc_table_embeddable.tsx +++ b/src/plugins/discover/public/components/doc_table/create_doc_table_embeddable.tsx @@ -30,7 +30,7 @@ export function DiscoverDocTableEmbeddable(renderProps: DocTableEmbeddableProps) searchDescription={renderProps.searchDescription} sharedItemTitle={renderProps.sharedItemTitle} isLoading={renderProps.isLoading} - isPlainRecord={renderProps.isPlainRecord} + isEsqlMode={renderProps.isEsqlMode} interceptedWarnings={renderProps.interceptedWarnings} /> ); diff --git a/src/plugins/discover/public/components/doc_table/doc_table_embeddable.tsx b/src/plugins/discover/public/components/doc_table/doc_table_embeddable.tsx index 0843d3baec295..58659b5d4b66a 100644 --- a/src/plugins/discover/public/components/doc_table/doc_table_embeddable.tsx +++ b/src/plugins/discover/public/components/doc_table/doc_table_embeddable.tsx @@ -29,7 +29,7 @@ export interface DocTableEmbeddableProps extends Omit; const DocTableWrapperMemoized = memo(DocTableWrapper); diff --git a/src/plugins/discover/public/components/doc_table/doc_table_wrapper.tsx b/src/plugins/discover/public/components/doc_table/doc_table_wrapper.tsx index 5ad815d7c1cc6..3c9d2fc96ae0d 100644 --- a/src/plugins/discover/public/components/doc_table/doc_table_wrapper.tsx +++ b/src/plugins/discover/public/components/doc_table/doc_table_wrapper.tsx @@ -62,9 +62,9 @@ export interface DocTableProps { filters?: Filter[]; /** * Flag which identifies if Discover operates - * in text based mode (ESQL) + * in ES|QL mode */ - isPlainRecord?: boolean; + isEsqlMode?: boolean; /** * Saved search id */ @@ -114,7 +114,7 @@ export const DocTableWrapper = forwardRef( render, columns, filters, - isPlainRecord, + isEsqlMode, savedSearchId, rows, dataView, @@ -186,7 +186,7 @@ export const DocTableWrapper = forwardRef( shouldShowFieldHandler={shouldShowFieldHandler} onAddColumn={onAddColumn} onRemoveColumn={onRemoveColumn} - isPlainRecord={isPlainRecord} + isEsqlMode={isEsqlMode} rows={rows} /> )); @@ -201,7 +201,7 @@ export const DocTableWrapper = forwardRef( shouldShowFieldHandler, onAddColumn, onRemoveColumn, - isPlainRecord, + isEsqlMode, rows, ] ); diff --git a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.test.tsx b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.test.tsx index 371ea1f0d06e2..afc59b6e7d0c6 100644 --- a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.test.tsx +++ b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.test.tsx @@ -23,7 +23,7 @@ describe('Document view mode toggle component', () => { const mountComponent = ({ showFieldStatistics = true, viewMode = VIEW_MODE.DOCUMENT_LEVEL, - isTextBasedQuery = false, + isEsqlMode = false, setDiscoverViewMode = jest.fn(), } = {}) => { const services = { @@ -43,7 +43,7 @@ describe('Document view mode toggle component', () => { @@ -63,8 +63,8 @@ describe('Document view mode toggle component', () => { expect(findTestSubject(component, 'discoverQueryTotalHits').exists()).toBe(true); }); - it('should not render if text-based', () => { - const component = mountComponent({ isTextBasedQuery: true }); + it('should not render if ES|QL', () => { + const component = mountComponent({ isEsqlMode: true }); expect(findTestSubject(component, 'dscViewModeToggle').exists()).toBe(false); expect(findTestSubject(component, 'discoverQueryTotalHits').exists()).toBe(true); }); diff --git a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx index d96249827c300..b9b036e266b4e 100644 --- a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx +++ b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx @@ -18,13 +18,13 @@ import { HitsCounter, HitsCounterMode } from '../hits_counter'; export const DocumentViewModeToggle = ({ viewMode, - isTextBasedQuery, + isEsqlMode, prepend, stateContainer, setDiscoverViewMode, }: { viewMode: VIEW_MODE; - isTextBasedQuery: boolean; + isEsqlMode: boolean; prepend?: ReactElement; stateContainer: DiscoverStateContainer; setDiscoverViewMode: (viewMode: VIEW_MODE) => void; @@ -32,8 +32,8 @@ export const DocumentViewModeToggle = ({ const { euiTheme } = useEuiTheme(); const { uiSettings, dataVisualizer: dataVisualizerService } = useDiscoverServices(); const isLegacy = useMemo( - () => isLegacyTableEnabled({ uiSettings, isTextBasedQueryMode: isTextBasedQuery }), - [uiSettings, isTextBasedQuery] + () => isLegacyTableEnabled({ uiSettings, isEsqlMode }), + [uiSettings, isEsqlMode] ); const includesNormalTabsStyle = viewMode === VIEW_MODE.AGGREGATED_LEVEL || isLegacy; @@ -72,7 +72,7 @@ export const DocumentViewModeToggle = ({ )} - {isTextBasedQuery || !showViewModeToggle ? ( + {isEsqlMode || !showViewModeToggle ? ( ) : ( diff --git a/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx b/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx index d5aac2dc246a8..06aeaa42c1376 100644 --- a/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx +++ b/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx @@ -13,6 +13,7 @@ import { Query, TimeRange, FilterStateStore, + isOfAggregateQueryType, } from '@kbn/es-query'; import React from 'react'; import ReactDOM, { unmountComponentAtNode } from 'react-dom'; @@ -66,8 +67,7 @@ import { SavedSearchEmbeddableComponent } from './saved_search_embeddable_compon import { handleSourceColumnState } from '../utils/state_helpers'; import { updateSearchSource } from './utils/update_search_source'; import { FieldStatisticsTable } from '../application/main/components/field_stats_table'; -import { fetchTextBased } from '../application/main/data_fetching/fetch_text_based'; -import { isTextBasedQuery } from '../application/main/utils/is_text_based_query'; +import { fetchEsql } from '../application/main/data_fetching/fetch_esql'; import { getValidViewMode } from '../application/main/utils/get_valid_view_mode'; import { ADHOC_DATA_VIEW_RENDER_EVENT } from '../constants'; import { getDiscoverLocatorParams } from './get_discover_locator_params'; @@ -229,9 +229,9 @@ export class SavedSearchEmbeddable return true; } - private isTextBasedSearch = (savedSearch: SavedSearch): boolean => { + private isEsqlMode = (savedSearch: SavedSearch): boolean => { const query = savedSearch.searchSource.getField('query'); - return isTextBasedQuery(query); + return isOfAggregateQueryType(query); }; private getFetchedSampleSize = (searchProps: SearchProps): number => { @@ -302,12 +302,12 @@ export class SavedSearchEmbeddable const query = savedSearch.searchSource.getField('query'); const dataView = savedSearch.searchSource.getField('index')!; - const useTextBased = this.isTextBasedSearch(savedSearch); + const isEsqlMode = this.isEsqlMode(savedSearch); try { - // Request text based data - if (useTextBased && query) { - const result = await fetchTextBased( + // Request ES|QL data + if (isEsqlMode && query) { + const result = await fetchEsql( savedSearch.searchSource.getField('query')!, dataView, this.services.data, @@ -323,8 +323,8 @@ export class SavedSearchEmbeddable loading: false, }); - searchProps.columnsMeta = result.textBasedQueryColumns - ? getTextBasedColumnsMeta(result.textBasedQueryColumns) + searchProps.columnsMeta = result.esqlQueryColumns + ? getTextBasedColumnsMeta(result.esqlQueryColumns) : undefined; searchProps.rows = result.records; searchProps.totalHitCount = result.records.length; @@ -392,9 +392,9 @@ export class SavedSearchEmbeddable private getSort( sort: SortPair[] | undefined, dataView: DataView | undefined, - isTextBasedQueryMode: boolean + isEsqlMode: boolean ) { - return getSortForEmbeddable(sort, dataView, this.services.uiSettings, isTextBasedQueryMode); + return getSortForEmbeddable(sort, dataView, this.services.uiSettings, isEsqlMode); } private initializeSearchEmbeddableProps() { @@ -421,7 +421,7 @@ export class SavedSearchEmbeddable filters: savedSearch.searchSource.getField('filter') as Filter[], dataView, isLoading: false, - sort: this.getSort(savedSearch.sort, dataView, this.isTextBasedSearch(savedSearch)), + sort: this.getSort(savedSearch.sort, dataView, this.isEsqlMode(savedSearch)), rows: [], searchDescription: savedSearch.description, description: savedSearch.description, @@ -460,7 +460,7 @@ export class SavedSearchEmbeddable this.updateInput({ sort: sortOrderArr }); }, // I don't want to create filters when is embedded - ...(!this.isTextBasedSearch(savedSearch) && { + ...(!this.isEsqlMode(savedSearch) && { onFilter: async (field, value, operator) => { let filters = generateFilters( this.services.filterManager, @@ -583,7 +583,7 @@ export class SavedSearchEmbeddable searchProps.sort = this.getSort( this.input.sort || savedSearch.sort, searchProps?.dataView, - this.isTextBasedSearch(savedSearch) + this.isEsqlMode(savedSearch) ); searchProps.sharedItemTitle = this.panelTitleInternal; searchProps.searchTitle = this.panelTitleInternal; @@ -640,10 +640,10 @@ export class SavedSearchEmbeddable return; } - const isTextBasedQueryMode = this.isTextBasedSearch(savedSearch); + const isEsqlMode = this.isEsqlMode(savedSearch); const viewMode = getValidViewMode({ viewMode: savedSearch.viewMode, - isTextBasedQueryMode, + isEsqlMode, }); if ( @@ -680,7 +680,7 @@ export class SavedSearchEmbeddable const useLegacyTable = isLegacyTableEnabled({ uiSettings: this.services.uiSettings, - isTextBasedQueryMode, + isEsqlMode, }); const query = savedSearch.searchSource.getField('query'); const props = { diff --git a/src/plugins/discover/public/embeddable/saved_search_embeddable_component.tsx b/src/plugins/discover/public/embeddable/saved_search_embeddable_component.tsx index 7c938db1018ac..6114adb414568 100644 --- a/src/plugins/discover/public/embeddable/saved_search_embeddable_component.tsx +++ b/src/plugins/discover/public/embeddable/saved_search_embeddable_component.tsx @@ -7,11 +7,10 @@ */ import React from 'react'; -import { AggregateQuery, Query } from '@kbn/es-query'; +import { AggregateQuery, isOfAggregateQueryType, Query } from '@kbn/es-query'; import { DataLoadingState } from '@kbn/unified-data-table'; import { DiscoverGridEmbeddable } from './saved_search_grid'; import { DiscoverDocTableEmbeddable } from '../components/doc_table/create_doc_table_embeddable'; -import { isTextBasedQuery } from '../application/main/utils/is_text_based_query'; import type { EmbeddableComponentSearchProps } from './types'; interface SavedSearchEmbeddableComponentProps { @@ -31,15 +30,15 @@ export function SavedSearchEmbeddableComponent({ query, }: SavedSearchEmbeddableComponentProps) { if (useLegacyTable) { - const isPlainRecord = isTextBasedQuery(query); return ( ); } + return ( { `); }); - test('fields do not have prepended timeField for text based languages', async () => { + test('fields do not have prepended timeField for ES|QL', async () => { const index = { ...dataViewMock } as DataView; index.timeFieldName = 'cool-timefield'; diff --git a/src/plugins/discover/public/utils/get_sharing_data.ts b/src/plugins/discover/public/utils/get_sharing_data.ts index b32f64cf79fb5..68e889c9b3cb0 100644 --- a/src/plugins/discover/public/utils/get_sharing_data.ts +++ b/src/plugins/discover/public/utils/get_sharing_data.ts @@ -34,7 +34,7 @@ export async function getSharingData( currentSearchSource: ISearchSource, state: DiscoverAppState | SavedSearch, services: { uiSettings: IUiSettingsClient; data: DataPublicPluginStart }, - isPlainRecord?: boolean + isEsqlMode?: boolean ) { const { uiSettings, data } = services; const searchSource = currentSearchSource.createCopy(); @@ -61,7 +61,7 @@ export async function getSharingData( // conditionally add the time field column: let timeFieldName: string | undefined; const hideTimeColumn = uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING); - if (!hideTimeColumn && index && index.timeFieldName && !isPlainRecord) { + if (!hideTimeColumn && index && index.timeFieldName && !isEsqlMode) { timeFieldName = index.timeFieldName; } if (timeFieldName && !columns.includes(timeFieldName)) { diff --git a/src/plugins/discover/public/utils/sorting/get_sort.ts b/src/plugins/discover/public/utils/sorting/get_sort.ts index 74d7fe4b7fb1e..3c6be442a3c6e 100644 --- a/src/plugins/discover/public/utils/sorting/get_sort.ts +++ b/src/plugins/discover/public/utils/sorting/get_sort.ts @@ -19,7 +19,7 @@ export function getSortForEmbeddable( sort: SortInput | undefined, dataView: DataView | undefined, uiSettings: IUiSettingsClient | undefined, - isTextBasedQueryMode: boolean + isEsqlMode: boolean ): SortOrder[] { if (!sort || !sort.length || !dataView) { if (!uiSettings) { @@ -27,7 +27,7 @@ export function getSortForEmbeddable( } const defaultSortOrder = uiSettings.get(SORT_DEFAULT_ORDER_SETTING, 'desc'); const hidingTimeColumn = uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING, false); - return getDefaultSort(dataView, defaultSortOrder, hidingTimeColumn, isTextBasedQueryMode); + return getDefaultSort(dataView, defaultSortOrder, hidingTimeColumn, isEsqlMode); } - return getSortArray(sort, dataView, isTextBasedQueryMode); + return getSortArray(sort, dataView, isEsqlMode); } diff --git a/src/plugins/expressions/common/execution/execution.ts b/src/plugins/expressions/common/execution/execution.ts index 13162b3a9d825..9dea349940ccb 100644 --- a/src/plugins/expressions/common/execution/execution.ts +++ b/src/plugins/expressions/common/execution/execution.ts @@ -27,7 +27,7 @@ import { Subscription, } from 'rxjs'; import { catchError, finalize, map, pluck, shareReplay, switchMap, tap } from 'rxjs'; -import { now, AbortError } from '@kbn/kibana-utils-plugin/common'; +import { now, AbortError, calculateObjectHash } from '@kbn/kibana-utils-plugin/common'; import { Adapters } from '@kbn/inspector-plugin/common'; import { Executor } from '../executor'; import { createExecutionContainer, ExecutionContainer } from './container'; @@ -55,6 +55,10 @@ type UnwrapReturnType unknown> = ? UnwrapObservable> : Awaited>; +export interface FunctionCacheItem { + value: unknown; + time: number; +} /** * The result returned after an expression function execution. */ @@ -70,6 +74,8 @@ export interface ExecutionResult { result: Output; } +const maxCacheSize = 1000; + const createAbortErrorValue = () => createError({ message: 'The expression was aborted.', @@ -235,6 +241,7 @@ export class Execution< * @private */ private readonly childExecutions: Execution[] = []; + private cacheTimeout: number = 30000; /** * Contract is a public representation of `Execution` instances. Contract we @@ -248,7 +255,11 @@ export class Execution< return this.context.inspectorAdapters; } - constructor(public readonly execution: ExecutionParams, private readonly logger?: Logger) { + constructor( + public readonly execution: ExecutionParams, + private readonly logger?: Logger, + private readonly functionCache: Map = new Map() + ) { const { executor } = execution; this.contract = new ExecutionContract(this); @@ -278,6 +289,7 @@ export class Execution< ? () => execution.params.kibanaRequest! : undefined, variables: execution.params.variables || {}, + allowCache: this.execution.params.allowCache, types: executor.getTypes(), abortSignal: this.abortController.signal, inspectorAdapters, @@ -454,42 +466,75 @@ export class Execution< input: unknown, args: Record ): Observable> { - return of(input).pipe( - map((currentInput) => this.cast(currentInput, fn.inputTypes)), - switchMap((normalizedInput) => of(fn.fn(normalizedInput, args, this.context))), - switchMap( - (fnResult) => - (isObservable(fnResult) - ? fnResult - : from(isPromise(fnResult) ? fnResult : [fnResult])) as Observable< - UnwrapReturnType - > - ), - map((output) => { - // Validate that the function returned the type it said it would. - // This isn't required, but it keeps function developers honest. - const returnType = getType(output); - const expectedType = fn.type; - if (expectedType && returnType !== expectedType) { - throw new Error( - `Function '${fn.name}' should return '${expectedType}',` + - ` actually returned '${returnType}'` - ); - } + let hash: string | undefined; + let lastValue: unknown; + let completionFlag = false; + + return of(input) + .pipe( + map((currentInput) => this.cast(currentInput, fn.inputTypes)), + switchMap((normalizedInput) => { + if (fn.allowCache && this.context.allowCache) { + hash = calculateObjectHash([ + fn.name, + normalizedInput, + args, + this.context.getSearchContext(), + ]); + } + if (hash && this.functionCache.has(hash)) { + const cached = this.functionCache.get(hash); + if (cached && Date.now() - cached.time < this.cacheTimeout) { + return of(cached.value); + } + } + return of(fn.fn(normalizedInput, args, this.context)); + }), + switchMap((fnResult) => { + return ( + isObservable(fnResult) ? fnResult : from(isPromise(fnResult) ? fnResult : [fnResult]) + ) as Observable>; + }), + map((output) => { + // Validate that the function returned the type it said it would. + // This isn't required, but it keeps function developers honest. + const returnType = getType(output); + const expectedType = fn.type; + if (expectedType && returnType !== expectedType) { + throw new Error( + `Function '${fn.name}' should return '${expectedType}',` + + ` actually returned '${returnType}'` + ); + } - // Validate the function output against the type definition's validate function. - const type = this.context.types[fn.type]; - if (type && type.validate) { - try { - type.validate(output); - } catch (e) { - throw new Error(`Output of '${fn.name}' is not a valid type '${fn.type}': ${e}`); + // Validate the function output against the type definition's validate function. + const type = this.context.types[fn.type]; + if (type && type.validate) { + try { + type.validate(output); + } catch (e) { + throw new Error(`Output of '${fn.name}' is not a valid type '${fn.type}': ${e}`); + } } - } - return output; - }) - ); + lastValue = output; + + return output; + }), + finalize(() => { + if (completionFlag && hash) { + while (this.functionCache.size >= maxCacheSize) { + this.functionCache.delete(this.functionCache.keys().next().value); + } + this.functionCache.set(hash, { value: lastValue, time: Date.now() }); + } + }) + ) + .pipe( + tap({ + complete: () => (completionFlag = true), // Set flag true only on successful completion + }) + ); } public cast(value: unknown, toTypeNames?: string[]): Type { diff --git a/src/plugins/expressions/common/execution/types.ts b/src/plugins/expressions/common/execution/types.ts index 03dbcc8a6ff13..4d8923003893a 100644 --- a/src/plugins/expressions/common/execution/types.ts +++ b/src/plugins/expressions/common/execution/types.ts @@ -35,6 +35,11 @@ export interface ExecutionContext */ types: Record; + /** + * Allow caching in the current execution. + */ + allowCache?: boolean; + /** * Adds ability to abort current execution. */ diff --git a/src/plugins/expressions/common/executor/executor.execution.test.ts b/src/plugins/expressions/common/executor/executor.execution.test.ts index ea906881e9738..44e6c92134b7e 100644 --- a/src/plugins/expressions/common/executor/executor.execution.test.ts +++ b/src/plugins/expressions/common/executor/executor.execution.test.ts @@ -27,7 +27,8 @@ describe('Executor mocked execution tests', () => { expect(Execution).toHaveBeenCalledWith( expect.objectContaining({ expression: 'foo bar="baz"' }), - undefined + undefined, + expect.anything() ); }); }); @@ -40,7 +41,8 @@ describe('Executor mocked execution tests', () => { expect(Execution).toHaveBeenCalledWith( expect.not.objectContaining({ expression: expect.anything() }), - undefined + undefined, + expect.anything() ); }); }); diff --git a/src/plugins/expressions/common/executor/executor.test.ts b/src/plugins/expressions/common/executor/executor.test.ts index 6f9dae491e5b2..de651e4c35f10 100644 --- a/src/plugins/expressions/common/executor/executor.test.ts +++ b/src/plugins/expressions/common/executor/executor.test.ts @@ -9,7 +9,7 @@ import { Executor } from './executor'; import * as expressionTypes from '../expression_types'; import * as expressionFunctions from '../expression_functions'; -import { Execution } from '../execution'; +import { Execution, FunctionCacheItem } from '../execution'; import { ExpressionAstFunction, parseExpression, formatExpression } from '../ast'; import { MigrateFunction } from '@kbn/kibana-utils-plugin/common/persistable_state'; import { SavedObjectReference } from '@kbn/core/types'; @@ -312,4 +312,92 @@ describe('Executor', () => { }); }); }); + + describe('caching', () => { + const functionCache: Map = new Map(); + const fakeCacheEntry = { time: Date.now(), value: 'test' }; + let executor: Executor; + + beforeAll(() => { + executor = new Executor(undefined, undefined, functionCache); + executor.registerFunction(expressionFunctions.variable); + expressionFunctions.theme.allowCache = true; + executor.registerFunction(expressionFunctions.theme); + }); + + afterEach(() => { + functionCache.clear(); + }); + + it('caches the result of function', async () => { + await executor.run('theme size default=12', null, { allowCache: true }).toPromise(); + expect(functionCache.size).toEqual(1); + const entry = functionCache.keys().next().value; + functionCache.set(entry, fakeCacheEntry); + const result = await executor + .run('theme size default=12', null, { allowCache: true }) + .toPromise(); + expect(functionCache.size).toEqual(1); + expect(result?.result).toEqual(fakeCacheEntry.value); + }); + + it('doesnt cache if allowCache flag is false', async () => { + await executor.run('theme size default=12', null, { allowCache: true }).toPromise(); + expect(functionCache.size).toEqual(1); + const entry = functionCache.keys().next().value; + functionCache.set(entry, fakeCacheEntry); + const result = await executor + .run('theme size default=12', null, { allowCache: false }) + .toPromise(); + expect(functionCache.size).toEqual(1); + expect(result?.result).not.toEqual(fakeCacheEntry.value); + }); + + it('doesnt cache results of functions that have allowCache property set to false', async () => { + await executor.run('var name="test"', null, { allowCache: true }).toPromise(); + expect(functionCache.size).toEqual(0); + }); + + describe('doesnt use cached version', () => { + const cachedVersion = { time: Date.now(), value: 'value' }; + + beforeAll(async () => { + await executor.run('theme size default=12', null, { allowCache: true }).toPromise(); + expect(functionCache.size).toEqual(1); + const entry: string = Object.keys(functionCache)[0]; + functionCache.set(entry, cachedVersion); + }); + + it('input changed', async () => { + const result = await executor + .run( + 'theme size default=12', + { + type: 'kibana_context', + value: 'test', + }, + { allowCache: true } + ) + .toPromise(); + expect(result).not.toEqual(cachedVersion); + }); + + it('arguments changed', async () => { + const result = await executor + .run('theme size default=14', null, { allowCache: true }) + .toPromise(); + expect(result).not.toEqual(cachedVersion); + }); + + it('search context changed', async () => { + const result = await executor + .run('theme size default=12', null, { + searchContext: { filters: [] }, + allowCache: true, + }) + .toPromise(); + expect(result).not.toEqual(cachedVersion); + }); + }); + }); }); diff --git a/src/plugins/expressions/common/executor/executor.ts b/src/plugins/expressions/common/executor/executor.ts index e8d01b15fd81d..28ccd1b95db1f 100644 --- a/src/plugins/expressions/common/executor/executor.ts +++ b/src/plugins/expressions/common/executor/executor.ts @@ -22,7 +22,12 @@ import { import { ExecutorState, ExecutorContainer } from './container'; import { createExecutorContainer } from './container'; import { AnyExpressionFunctionDefinition, ExpressionFunction } from '../expression_functions'; -import { Execution, ExecutionParams, ExecutionResult } from '../execution/execution'; +import { + Execution, + ExecutionParams, + ExecutionResult, + FunctionCacheItem, +} from '../execution/execution'; import { IRegistry } from '../types'; import { ExpressionType } from '../expression_types/expression_type'; import { AnyExpressionTypeDefinition } from '../expression_types/types'; @@ -109,10 +114,17 @@ export class Executor = Record) { + private functionCache: Map; + + constructor( + private readonly logger?: Logger, + state?: ExecutorState, + functionCache: Map = new Map() + ) { this.functions = new FunctionsRegistry(this as Executor); this.types = new TypesRegistry(this as Executor); this.container = createExecutorContainer(state); + this.functionCache = functionCache; } public get state(): ExecutorState { @@ -189,12 +201,17 @@ export class Executor = Record(executionParams, this.logger); + const execution = new Execution( + executionParams, + this.logger, + this.functionCache + ); return execution; } diff --git a/src/plugins/expressions/common/expression_functions/expression_function.ts b/src/plugins/expressions/common/expression_functions/expression_function.ts index 7ce51e3a7d36d..5df2e40a8286d 100644 --- a/src/plugins/expressions/common/expression_functions/expression_function.ts +++ b/src/plugins/expressions/common/expression_functions/expression_function.ts @@ -38,6 +38,11 @@ export class ExpressionFunction implements PersistableState c); diff --git a/src/plugins/expressions/common/expression_functions/types.ts b/src/plugins/expressions/common/expression_functions/types.ts index c59169ccf04ab..7e14d4ba72f30 100644 --- a/src/plugins/expressions/common/expression_functions/types.ts +++ b/src/plugins/expressions/common/expression_functions/types.ts @@ -57,6 +57,11 @@ export interface ExpressionFunctionDefinition< */ type?: TypeString | UnmappedTypeStrings; + /** + * Opt-in to caching this function. By default function outputs are cached and given the same inputs cached result is returned. + */ + allowCache?: boolean; + /** * List of allowed type names for input value of this function. If this * property is set the input of function will be cast to the first possible diff --git a/src/plugins/expressions/common/mocks.ts b/src/plugins/expressions/common/mocks.ts index 4141da06ec04e..db880a4eaa5d2 100644 --- a/src/plugins/expressions/common/mocks.ts +++ b/src/plugins/expressions/common/mocks.ts @@ -28,6 +28,7 @@ export const createMockExecutionContext = requests: {}, data: {}, }, + allowCache: false, } as unknown as ExecutionContext; return { diff --git a/src/plugins/expressions/common/service/expressions_services.ts b/src/plugins/expressions/common/service/expressions_services.ts index e73e07a387c46..d28703b40145b 100644 --- a/src/plugins/expressions/common/service/expressions_services.ts +++ b/src/plugins/expressions/common/service/expressions_services.ts @@ -173,6 +173,8 @@ export interface ExpressionExecutionParams { * @deafult 0 */ throttle?: number; + + allowCache?: boolean; } /** diff --git a/src/plugins/expressions/public/loader.ts b/src/plugins/expressions/public/loader.ts index eb45b581c29a2..11ed32c4633a9 100644 --- a/src/plugins/expressions/public/loader.ts +++ b/src/plugins/expressions/public/loader.ts @@ -151,6 +151,7 @@ export class ExpressionLoader { executionContext: params.executionContext, partial: params.partial, throttle: params.throttle, + allowCache: params.allowCache, }); this.subscription = this.execution .getData() @@ -190,6 +191,7 @@ export class ExpressionLoader { this.params.debug = Boolean(params.debug); this.params.partial = Boolean(params.partial); this.params.throttle = Number(params.throttle ?? 1000); + this.params.allowCache = params.allowCache; this.params.inspectorAdapters = (params.inspectorAdapters || this.execution?.inspect()) as Adapters; diff --git a/src/plugins/expressions/public/types/index.ts b/src/plugins/expressions/public/types/index.ts index c1b7dc8cc6369..8f6a7b9dc63b3 100644 --- a/src/plugins/expressions/public/types/index.ts +++ b/src/plugins/expressions/public/types/index.ts @@ -40,7 +40,6 @@ export interface IExpressionLoaderParams { variables?: Record; // Enables debug tracking on each expression in the AST debug?: boolean; - disableCaching?: boolean; customFunctions?: []; customRenderers?: []; uiState?: unknown; @@ -67,6 +66,8 @@ export interface IExpressionLoaderParams { * By default, it equals 1000. */ throttle?: number; + + allowCache?: boolean; } export interface ExpressionRenderError extends Error { diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/get_height.test.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/get_height.test.tsx index 6c6ae2852e053..e28df4d89d5bc 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/get_height.test.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/get_height.test.tsx @@ -8,6 +8,7 @@ import { monaco } from '@kbn/monaco'; import { getHeight } from './get_height'; +import { MARGIN_BOTTOM } from './source'; describe('getHeight', () => { Object.defineProperty(window, 'innerHeight', { writable: true, configurable: true, value: 500 }); @@ -32,7 +33,7 @@ describe('getHeight', () => { const monacoMock = getMonacoMock(500, 0); const height = getHeight(monacoMock, true); - expect(height).toBe(475); + expect(height).toBe(500 - MARGIN_BOTTOM); }); test('when using document explorer, returning the available height in the flyout has a minimun guarenteed height', () => { diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/source.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/source.tsx index 13f2ee065f504..5596ca067df5f 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/source.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/source.tsx @@ -36,7 +36,7 @@ interface SourceViewerProps { // inline limitation was necessary to enable virtualized scrolling, which improves performance export const MAX_LINES_CLASSIC_TABLE = 500; // Displayed margin of the code editor to the window bottom when rendered in the document explorer flyout -export const MARGIN_BOTTOM = 25; +export const MARGIN_BOTTOM = 80; // DocViewer flyout has a footer // Minimum height for the source content to guarantee minimum space when the flyout is scrollable. export const MIN_HEIGHT = 400; @@ -56,7 +56,7 @@ export const DocViewerSource = ({ const useNewFieldsApi = !uiSettings.get(SEARCH_FIELDS_FROM_SOURCE); const useDocExplorer = !isLegacyTableEnabled({ uiSettings, - isTextBasedQueryMode: Array.isArray(textBasedHits), + isEsqlMode: Array.isArray(textBasedHits), }); const [requestState, hit] = useEsDocSearch({ id, diff --git a/src/plugins/unified_doc_viewer/public/plugin.tsx b/src/plugins/unified_doc_viewer/public/plugin.tsx index 1fb690694096d..5d26f43892ae1 100644 --- a/src/plugins/unified_doc_viewer/public/plugin.tsx +++ b/src/plugins/unified_doc_viewer/public/plugin.tsx @@ -82,7 +82,7 @@ export class UnifiedDocViewerPublicPlugin const LazyDocView = isLegacyTableEnabled({ uiSettings, - isTextBasedQueryMode: Array.isArray(textBasedHits), + isEsqlMode: Array.isArray(textBasedHits), }) ? LazyDocViewerLegacyTable : LazyDocViewerTable; diff --git a/test/api_integration/apis/data_views/fields_route/cache.ts b/test/api_integration/apis/data_views/fields_route/cache.ts index c97be4b7411f3..dea14dec6bdcd 100644 --- a/test/api_integration/apis/data_views/fields_route/cache.ts +++ b/test/api_integration/apis/data_views/fields_route/cache.ts @@ -70,7 +70,7 @@ export default function ({ getService }: FtrProviderContext) { await supertest .get(FIELDS_PATH) - .set('If-None-Match', response.get('etag')) + .set('If-None-Match', response.get('etag')!) .query({ pattern: '*', include_unmapped: true, diff --git a/test/api_integration/apis/saved_objects_management/scroll_count.ts b/test/api_integration/apis/saved_objects_management/scroll_count.ts index 94e301a085d70..26f4f4e1a626e 100644 --- a/test/api_integration/apis/saved_objects_management/scroll_count.ts +++ b/test/api_integration/apis/saved_objects_management/scroll_count.ts @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -import { SuperTest, Test } from 'supertest'; import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -14,7 +13,7 @@ const apiUrl = '/api/kibana/management/saved_objects/scroll/counts'; const defaultTypes = ['visualization', 'index-pattern', 'search', 'dashboard']; export default function ({ getService }: FtrProviderContext) { - const supertest = getService('supertest') as SuperTest; + const supertest = getService('supertest'); const kibanaServer = getService('kibanaServer'); const esArchiver = getService('esArchiver'); diff --git a/test/api_integration/apis/telemetry/opt_in.ts b/test/api_integration/apis/telemetry/opt_in.ts index c7d8a42c6e392..a9fc5861368ca 100644 --- a/test/api_integration/apis/telemetry/opt_in.ts +++ b/test/api_integration/apis/telemetry/opt_in.ts @@ -87,7 +87,7 @@ export default function optInTest({ getService }: FtrProviderContext) { } async function postTelemetryV2OptIn( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, value: unknown, statusCode: number ): Promise { diff --git a/test/api_integration/services/supertest.ts b/test/api_integration/services/supertest.ts index baf42703c64a0..d8ce0d918c45a 100644 --- a/test/api_integration/services/supertest.ts +++ b/test/api_integration/services/supertest.ts @@ -13,13 +13,15 @@ import { format as formatUrl } from 'url'; import supertest from 'supertest'; import { FtrProviderContext } from '../../functional/ftr_provider_context'; -export function KibanaSupertestProvider({ getService }: FtrProviderContext) { +export function KibanaSupertestProvider({ getService }: FtrProviderContext): supertest.Agent { const config = getService('config'); const kibanaServerUrl = formatUrl(config.get('servers.kibana')); return supertest(kibanaServerUrl); } -export function ElasticsearchSupertestProvider({ getService }: FtrProviderContext) { +export function ElasticsearchSupertestProvider({ + getService, +}: FtrProviderContext): supertest.Agent { const config = getService('config'); const esServerConfig = config.get('servers.elasticsearch'); diff --git a/test/common/services/bsearch.ts b/test/common/services/bsearch.ts index fd79a9b9a75e8..fd9ef7f703a0b 100644 --- a/test/common/services/bsearch.ts +++ b/test/common/services/bsearch.ts @@ -40,7 +40,7 @@ const getSpaceUrlPrefix = (spaceId?: string): string => { * Options for the send method */ interface SendOptions { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; options: object; strategy: string; space?: string; diff --git a/test/functional/apps/discover/group3/_lens_vis.ts b/test/functional/apps/discover/group3/_lens_vis.ts index a01760da64658..cc48864976811 100644 --- a/test/functional/apps/discover/group3/_lens_vis.ts +++ b/test/functional/apps/discover/group3/_lens_vis.ts @@ -179,7 +179,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ); }); - it('should show ESQL histogram for text-based query', async () => { + it('should show ESQL histogram for ES|QL query', async () => { await PageObjects.discover.selectTextBaseLang(); await monacoEditor.setCodeEditorValue('from logstash-* | limit 10'); diff --git a/test/functional/apps/discover/group3/_panels_toggle.ts b/test/functional/apps/discover/group3/_panels_toggle.ts index cbac15a3e45de..8b19fb5cc83c5 100644 --- a/test/functional/apps/discover/group3/_panels_toggle.ts +++ b/test/functional/apps/discover/group3/_panels_toggle.ts @@ -218,7 +218,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { checkPanelsToggle({ isChartAvailable: false, totalHits: '14,004' }); }); - describe('text-based with histogram chart', function () { + describe('ES|QL with histogram chart', function () { before(async function () { await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); await kibanaServer.uiSettings.update(defaultSettings); @@ -231,7 +231,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { checkPanelsToggle({ isChartAvailable: true, totalHits: '10' }); }); - describe('text-based with aggs chart', function () { + describe('ES|QL with aggs chart', function () { before(async function () { await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); await kibanaServer.uiSettings.update(defaultSettings); @@ -249,7 +249,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { checkPanelsToggle({ isChartAvailable: true, totalHits: '5' }); }); - describe('text-based without a time field', function () { + describe('ES|QL without a time field', function () { before(async function () { await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); await kibanaServer.uiSettings.update(defaultSettings); diff --git a/test/functional/apps/discover/group6/_sidebar.ts b/test/functional/apps/discover/group6/_sidebar.ts index aac4bd60933f0..9f7b35abc85da 100644 --- a/test/functional/apps/discover/group6/_sidebar.ts +++ b/test/functional/apps/discover/group6/_sidebar.ts @@ -96,7 +96,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); - it('should show filters by type in text-based view', async function () { + it('should show filters by type in ES|QL view', async function () { await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded(); await PageObjects.unifiedFieldList.openSidebarFieldFilter(); let options = await find.allByCssSelector('[data-test-subj*="typeFilter"]'); @@ -128,7 +128,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); - it('should show empty fields in text-based view', async function () { + it('should show empty fields in ES|QL view', async function () { await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded(); await PageObjects.discover.selectTextBaseLang(); @@ -427,7 +427,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ); }); - it('should show selected and available fields in text-based mode', async function () { + it('should show selected and available fields in ES|QL mode', async function () { await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded(); expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be( diff --git a/test/functional/apps/discover/group6/_sidebar_field_stats.ts b/test/functional/apps/discover/group6/_sidebar_field_stats.ts index b2526445f75bf..cbeb128036ab6 100644 --- a/test/functional/apps/discover/group6/_sidebar_field_stats.ts +++ b/test/functional/apps/discover/group6/_sidebar_field_stats.ts @@ -143,7 +143,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); - describe('text based columns', function () { + describe('ES|QL columns', function () { beforeEach(async () => { await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); await PageObjects.common.navigateToApp('discover'); diff --git a/test/functional/apps/discover/group6/_time_field_column.ts b/test/functional/apps/discover/group6/_time_field_column.ts index cbcb37a1294e7..6fe4c244a08b3 100644 --- a/test/functional/apps/discover/group6/_time_field_column.ts +++ b/test/functional/apps/discover/group6/_time_field_column.ts @@ -57,12 +57,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { hasTimeField, hideTimeFieldColumnSetting, savedSearchSuffix, - isTextBased, + isEsqlMode, }: { hasTimeField: boolean; hideTimeFieldColumnSetting: boolean; savedSearchSuffix: string; - isTextBased?: boolean; + isEsqlMode?: boolean; }) { // check in Discover expect(await dataGrid.getHeaderFields()).to.eql( @@ -71,7 +71,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.discover.saveSearch(`${SEARCH_NO_COLUMNS}${savedSearchSuffix}`); await PageObjects.discover.waitUntilSearchingHasFinished(); - const isTimestampUnavailableInSidebar = isTextBased && !hasTimeField; + const isTimestampUnavailableInSidebar = isEsqlMode && !hasTimeField; if (!isTimestampUnavailableInSidebar) { await PageObjects.unifiedFieldList.clickFieldListItemAdd('@timestamp'); await PageObjects.discover.waitUntilSearchingHasFinished(); @@ -137,12 +137,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { hasTimeField, hideTimeFieldColumnSetting, savedSearchSuffix, - isTextBased, + isEsqlMode, }: { hasTimeField: boolean; hideTimeFieldColumnSetting: boolean; savedSearchSuffix: string; - isTextBased?: boolean; + isEsqlMode?: boolean; }) { // check in Discover await PageObjects.unifiedFieldList.clickFieldListItemAdd('bytes'); @@ -150,7 +150,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await retry.try(async () => { expect(await dataGrid.getHeaderFields()).to.eql( - hideTimeFieldColumnSetting || !hasTimeField || isTextBased + hideTimeFieldColumnSetting || !hasTimeField || isEsqlMode ? ['bytes', 'extension'] : ['@timestamp', 'bytes', 'extension'] ); @@ -183,7 +183,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.unifiedFieldList.clickFieldListItemRemove('@timestamp'); await retry.try(async () => { expect(await dataGrid.getHeaderFields()).to.eql( - hideTimeFieldColumnSetting || !hasTimeField || isTextBased + hideTimeFieldColumnSetting || !hasTimeField || isEsqlMode ? ['bytes', 'extension'] : ['@timestamp', 'bytes', 'extension'] ); @@ -199,7 +199,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await retry.try(async () => { expect(await dataGrid.getHeaderFields()).to.eql( - hideTimeFieldColumnSetting || !hasTimeField || isTextBased + hideTimeFieldColumnSetting || !hasTimeField || isEsqlMode ? ['bytes', 'extension'] : ['@timestamp', 'bytes', 'extension'] ); @@ -292,7 +292,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { hasTimeField: true, hideTimeFieldColumnSetting, savedSearchSuffix: savedSearchSuffix + 'ESQL', - isTextBased: true, + isEsqlMode: true, }); }); @@ -306,7 +306,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { hasTimeField: false, hideTimeFieldColumnSetting, savedSearchSuffix: savedSearchSuffix + 'ESQLdrop', - isTextBased: true, + isEsqlMode: true, }); }); @@ -317,7 +317,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { hasTimeField: true, hideTimeFieldColumnSetting, savedSearchSuffix: savedSearchSuffix + 'ESQL', - isTextBased: true, + isEsqlMode: true, }); }); }); diff --git a/test/functional/apps/discover/group6/_view_mode_toggle.ts b/test/functional/apps/discover/group6/_view_mode_toggle.ts index 935b75bf61937..eada1ec26f7aa 100644 --- a/test/functional/apps/discover/group6/_view_mode_toggle.ts +++ b/test/functional/apps/discover/group6/_view_mode_toggle.ts @@ -99,7 +99,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.existOrFail('dscViewModeToggle'); }); - it('should not show view mode toggle for text-based searches', async () => { + it('should not show view mode toggle for ES|QL searches', async () => { await testSubjects.click('dscViewModeDocumentButton'); await retry.try(async () => { @@ -118,7 +118,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { } }); - it('should show text-based columns callout', async () => { + it('should show ES|QL columns callout', async () => { await testSubjects.missingOrFail('dscSelectedColumnsCallout'); await PageObjects.unifiedFieldList.clickFieldListItemAdd('extension'); await PageObjects.header.waitUntilLoadingHasFinished(); diff --git a/tsconfig.base.json b/tsconfig.base.json index f87d111ac27af..dd82d55b1b0ad 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -774,6 +774,8 @@ "@kbn/encrypted-saved-objects-plugin/*": ["x-pack/plugins/encrypted_saved_objects/*"], "@kbn/enterprise-search-plugin": ["x-pack/plugins/enterprise_search"], "@kbn/enterprise-search-plugin/*": ["x-pack/plugins/enterprise_search/*"], + "@kbn/entities-schema": ["x-pack/packages/kbn-entities-schema"], + "@kbn/entities-schema/*": ["x-pack/packages/kbn-entities-schema/*"], "@kbn/error-boundary-example-plugin": ["examples/error_boundary"], "@kbn/error-boundary-example-plugin/*": ["examples/error_boundary/*"], "@kbn/es": ["packages/kbn-es"], diff --git a/x-pack/packages/kbn-data-forge/index.ts b/x-pack/packages/kbn-data-forge/index.ts index bb9d0e104f4a7..056c63e9964a6 100644 --- a/x-pack/packages/kbn-data-forge/index.ts +++ b/x-pack/packages/kbn-data-forge/index.ts @@ -19,3 +19,4 @@ export { cli } from './src/cli'; export { generate } from './src/generate'; export { cleanup } from './src/cleanup'; export { createConfig, readConfig } from './src/lib/create_config'; +export { DEFAULTS } from './src/constants'; diff --git a/x-pack/packages/kbn-data-forge/src/cli.ts b/x-pack/packages/kbn-data-forge/src/cli.ts index 9d7e9e3bca026..88a8aad6bce8d 100644 --- a/x-pack/packages/kbn-data-forge/src/cli.ts +++ b/x-pack/packages/kbn-data-forge/src/cli.ts @@ -6,14 +6,15 @@ */ import { ToolingLog } from '@kbn/tooling-log'; +import { CliOptions } from './types'; import { cliOptionsToPartialConfig } from './lib/cli_to_partial_config'; import { createConfig, readConfig } from './lib/create_config'; import { getEsClient } from './lib/get_es_client'; import { parseCliOptions } from './lib/parse_cli_options'; import { run } from './run'; -export async function cli() { - const options = parseCliOptions(); +export async function cli(cliOptions?: CliOptions) { + const options = cliOptions ?? parseCliOptions(); const partialConfig = options.config ? await readConfig(options.config) : cliOptionsToPartialConfig(options); diff --git a/x-pack/packages/kbn-data-forge/src/constants.ts b/x-pack/packages/kbn-data-forge/src/constants.ts index 0b5ab1978d983..afcc715916ab5 100644 --- a/x-pack/packages/kbn-data-forge/src/constants.ts +++ b/x-pack/packages/kbn-data-forge/src/constants.ts @@ -34,5 +34,5 @@ export const DEFAULTS = { EVENT_TEMPLATE: 'good', REDUCE_WEEKEND_TRAFFIC_BY: 0, EPHEMERAL_PROJECT_IDS: 0, - ALIGN_EVENTS_TO_INTERVAL: 0, + ALIGN_EVENTS_TO_INTERVAL: true, }; diff --git a/x-pack/packages/kbn-data-forge/src/lib/cli_to_partial_config.ts b/x-pack/packages/kbn-data-forge/src/lib/cli_to_partial_config.ts index b116f262303db..5d7d10d787bdc 100644 --- a/x-pack/packages/kbn-data-forge/src/lib/cli_to_partial_config.ts +++ b/x-pack/packages/kbn-data-forge/src/lib/cli_to_partial_config.ts @@ -14,7 +14,7 @@ export function cliOptionsToPartialConfig(options: CliOptions) { const schedule: Schedule = { template: options.eventTemplate, start: options.lookback, - end: false, + end: options.scheduleEnd ?? false, }; const decodedDataset = DatasetRT.decode(options.dataset); diff --git a/x-pack/packages/kbn-data-forge/src/lib/create_config.ts b/x-pack/packages/kbn-data-forge/src/lib/create_config.ts index c8a48b2632998..56b59b7d01365 100644 --- a/x-pack/packages/kbn-data-forge/src/lib/create_config.ts +++ b/x-pack/packages/kbn-data-forge/src/lib/create_config.ts @@ -62,7 +62,7 @@ export function createConfig(partialConfig: PartialConfig = {}) { concurrency: DEFAULTS.CONCURRENCY, reduceWeekendTrafficBy: DEFAULTS.REDUCE_WEEKEND_TRAFFIC_BY, ephemeralProjectIds: DEFAULTS.EPHEMERAL_PROJECT_IDS, - alignEventsToInterval: DEFAULTS.ALIGN_EVENTS_TO_INTERVAL === 1, + alignEventsToInterval: DEFAULTS.ALIGN_EVENTS_TO_INTERVAL, ...(partialConfig.indexing ?? {}), }, schedule: partialConfig.schedule ?? [schedule], diff --git a/x-pack/packages/kbn-data-forge/src/lib/index_schedule.ts b/x-pack/packages/kbn-data-forge/src/lib/index_schedule.ts index 916efe551dfc4..a18ce031c19a1 100644 --- a/x-pack/packages/kbn-data-forge/src/lib/index_schedule.ts +++ b/x-pack/packages/kbn-data-forge/src/lib/index_schedule.ts @@ -52,7 +52,7 @@ export async function indexSchedule(config: Config, client: Client, logger: Tool logger.info( `Indexing "${schedule.template}" events from ${startTs.toISOString()} to ${ - end === false ? 'indefinatly' : end.toISOString() + end === false ? 'indefinitely' : end.toISOString() }` ); await createEvents( diff --git a/x-pack/packages/kbn-data-forge/src/types/index.ts b/x-pack/packages/kbn-data-forge/src/types/index.ts index b84805d8ecb93..ac445478329c2 100644 --- a/x-pack/packages/kbn-data-forge/src/types/index.ts +++ b/x-pack/packages/kbn-data-forge/src/types/index.ts @@ -159,7 +159,7 @@ export interface Point { } export interface CliOptions { - config: string; + config?: string; lookback: string; eventsPerCycle: number; payloadSize: number; @@ -170,7 +170,7 @@ export interface CliOptions { elasticsearchHost: string; elasticsearchUsername: string; elasticsearchPassword: string; - elasticsearchApiKey: undefined | string; + elasticsearchApiKey?: undefined | string; kibanaUrl: string; kibanaUsername: string; kibanaPassword: string; @@ -179,4 +179,5 @@ export interface CliOptions { reduceWeekendTrafficBy: number; ephemeralProjectIds: number; alignEventsToInterval: boolean; + scheduleEnd?: string; } diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_assistant_overlay/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_assistant_overlay/index.test.tsx index c49db8d6246a3..9e21d9da57d74 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_assistant_overlay/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_assistant_overlay/index.test.tsx @@ -92,6 +92,7 @@ describe('useAssistantOverlay', () => { const id = 'test-id'; const suggestedUserPrompt = 'test user prompt'; const tooltip = 'test tooltip'; + const isAssistantAvailable = true; renderHook(() => useAssistantOverlay( @@ -101,7 +102,8 @@ describe('useAssistantOverlay', () => { getPromptContext, id, suggestedUserPrompt, - tooltip + tooltip, + isAssistantAvailable ) ); @@ -118,6 +120,7 @@ describe('useAssistantOverlay', () => { }); it('calls unRegisterPromptContext on unmount', () => { + const isAssistantAvailable = true; const { unmount } = renderHook(() => useAssistantOverlay( 'event', @@ -126,7 +129,8 @@ describe('useAssistantOverlay', () => { () => Promise.resolve('data'), 'id', null, - 'tooltip' + 'tooltip', + isAssistantAvailable ) ); @@ -136,6 +140,7 @@ describe('useAssistantOverlay', () => { }); it('calls `showAssistantOverlay` from the assistant context', () => { + const isAssistantAvailable = true; const { result } = renderHook(() => useAssistantOverlay( 'event', @@ -144,7 +149,8 @@ describe('useAssistantOverlay', () => { () => Promise.resolve('data'), 'id', null, - 'tooltip' + 'tooltip', + isAssistantAvailable ) ); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_assistant_overlay/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_assistant_overlay/index.tsx index 8b8a399bc7671..3396223d192ca 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_assistant_overlay/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_assistant_overlay/index.tsx @@ -74,6 +74,9 @@ export const useAssistantOverlay = ( */ tooltip: PromptContext['tooltip'], + /** Required to identify the availability of the Assistant for the current license level */ + isAssistantEnabled: boolean, + /** * Optionally provide a map of replacements associated with the context, i.e. replacements for an attack discovery that's provided as context */ @@ -96,7 +99,7 @@ export const useAssistantOverlay = ( const { data: conversations, isLoading } = useFetchCurrentUserConversations({ http, onFetch: onFetchedConversations, - isAssistantEnabled: true, + isAssistantEnabled, }); // memoize the props so that we can use them in the effect below: @@ -135,7 +138,7 @@ export const useAssistantOverlay = ( : undefined; } - if (!conversation && defaultConnector && !isLoading) { + if (isAssistantEnabled && !conversation && defaultConnector && !isLoading) { try { conversation = await createConversation({ apiConfig: { @@ -166,6 +169,7 @@ export const useAssistantOverlay = ( conversations, createConversation, defaultConnector, + isAssistantEnabled, isLoading, promptContextId, ] diff --git a/x-pack/packages/kbn-elastic-assistant/impl/new_chat/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/new_chat/index.test.tsx index 6850c84bf67b4..730c88413833d 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/new_chat/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/new_chat/index.test.tsx @@ -23,6 +23,7 @@ const defaultProps: Props = { description: 'Test description', getPromptContext: () => Promise.resolve('Test prompt context'), tooltip: 'Test tooltip', + isAssistantEnabled: true, }; describe('NewChat', () => { @@ -38,6 +39,14 @@ describe('NewChat', () => { expect(newChatButton.querySelector('[data-euiicon-type="discuss"]')).toBeInTheDocument(); }); + it('renders the default New Chat button even if the Assistant is disabled', () => { + render(); + + const newChatButton = screen.getByTestId('newChat'); + + expect(newChatButton.querySelector('[data-euiicon-type="discuss"]')).toBeInTheDocument(); + }); + it('renders the default "New Chat" text when children are NOT provided', () => { render(); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/new_chat/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/new_chat/index.tsx index 3fd798f38c4b9..a4793dfd25a9d 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/new_chat/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/new_chat/index.tsx @@ -23,6 +23,8 @@ export type Props = Omit & { promptContextId?: string; /** Optionally specify color of empty button */ color?: 'text' | 'accent' | 'primary' | 'success' | 'warning' | 'danger'; + /** Required to identify the availability of the Assistant for the current license level */ + isAssistantEnabled: boolean; }; const NewChatComponent: React.FC = ({ @@ -36,6 +38,7 @@ const NewChatComponent: React.FC = ({ promptContextId, suggestedUserPrompt, tooltip, + isAssistantEnabled, }) => { const { showAssistantOverlay } = useAssistantOverlay( category, @@ -44,7 +47,8 @@ const NewChatComponent: React.FC = ({ getPromptContext, promptContextId ?? null, suggestedUserPrompt, - tooltip + tooltip, + isAssistantEnabled ); const showOverlay = useCallback(() => { diff --git a/x-pack/packages/kbn-entities-schema/README.md b/x-pack/packages/kbn-entities-schema/README.md new file mode 100644 index 0000000000000..2601be6543c58 --- /dev/null +++ b/x-pack/packages/kbn-entities-schema/README.md @@ -0,0 +1,3 @@ +# @kbn/entities-schema + +The entities schema for the asset model for Observability \ No newline at end of file diff --git a/x-pack/packages/kbn-entities-schema/index.ts b/x-pack/packages/kbn-entities-schema/index.ts new file mode 100644 index 0000000000000..92b93b7938125 --- /dev/null +++ b/x-pack/packages/kbn-entities-schema/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './src/schema/entity_definition'; +export * from './src/schema/entity'; +export * from './src/schema/common'; diff --git a/x-pack/packages/kbn-entities-schema/jest.config.js b/x-pack/packages/kbn-entities-schema/jest.config.js new file mode 100644 index 0000000000000..1d10119431ec3 --- /dev/null +++ b/x-pack/packages/kbn-entities-schema/jest.config.js @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/packages/kbn-entities-schema'], +}; diff --git a/x-pack/packages/kbn-entities-schema/kibana.jsonc b/x-pack/packages/kbn-entities-schema/kibana.jsonc new file mode 100644 index 0000000000000..9895c2074a584 --- /dev/null +++ b/x-pack/packages/kbn-entities-schema/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/entities-schema", + "owner": "@elastic/obs-knowledge-team" +} diff --git a/x-pack/packages/kbn-entities-schema/package.json b/x-pack/packages/kbn-entities-schema/package.json new file mode 100644 index 0000000000000..0be44d9d75055 --- /dev/null +++ b/x-pack/packages/kbn-entities-schema/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/entities-schema", + "private": true, + "version": "1.0.0", + "license": "Elastic License 2.0" +} \ No newline at end of file diff --git a/x-pack/packages/kbn-entities-schema/src/schema/common.ts b/x-pack/packages/kbn-entities-schema/src/schema/common.ts new file mode 100644 index 0000000000000..6b4f0cc794c2b --- /dev/null +++ b/x-pack/packages/kbn-entities-schema/src/schema/common.ts @@ -0,0 +1,106 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { z } from 'zod'; +import moment from 'moment'; + +export enum EntityType { + service = 'service', + host = 'host', + pod = 'pod', + node = 'node', +} + +export const arrayOfStringsSchema = z.array(z.string()); + +export const entityTypeSchema = z.nativeEnum(EntityType); + +export enum BasicAggregations { + avg = 'avg', + max = 'max', + min = 'min', + sum = 'sum', + cardinality = 'cardinality', + last_value = 'last_value', + std_deviation = 'std_deviation', +} + +export const basicAggregationsSchema = z.nativeEnum(BasicAggregations); + +const metricNameSchema = z + .string() + .length(1) + .regex(/[a-zA-Z]/) + .toUpperCase(); + +export const filterSchema = z.optional(z.string()); + +export const basicMetricWithFieldSchema = z.object({ + name: metricNameSchema, + aggregation: basicAggregationsSchema, + field: z.string(), + filter: filterSchema, +}); + +export const docCountMetricSchema = z.object({ + name: metricNameSchema, + aggregation: z.literal('doc_count'), + filter: filterSchema, +}); + +export const durationSchema = z + .string() + .regex(/\d+[m|d|s|h]/) + .transform((val: string) => { + const parts = val.match(/(\d+)([m|s|h|d])/); + if (parts === null) { + throw new Error('Unable to parse duration'); + } + const value = parseInt(parts[1], 10); + const unit = parts[2] as 'm' | 's' | 'h' | 'd'; + const duration = moment.duration(value, unit); + return { ...duration, toJSON: () => val }; + }); + +export const percentileMetricSchema = z.object({ + name: metricNameSchema, + aggregation: z.literal('percentile'), + field: z.string(), + percentile: z.number(), + filter: filterSchema, +}); + +export const metricSchema = z.discriminatedUnion('aggregation', [ + basicMetricWithFieldSchema, + docCountMetricSchema, + percentileMetricSchema, +]); + +export type Metric = z.infer; + +export const keyMetricSchema = z.object({ + name: z.string(), + metrics: z.array(metricSchema), + equation: z.string(), +}); + +export type KeyMetric = z.infer; + +export const metadataSchema = z + .object({ + source: z.string(), + destination: z.optional(z.string()), + limit: z.optional(z.number().default(1000)), + }) + .or(z.string().transform((value) => ({ source: value, destination: value, limit: 1000 }))); + +export const identityFieldsSchema = z + .object({ + field: z.string(), + optional: z.boolean(), + }) + .or(z.string().transform((value) => ({ field: value, optional: false }))); diff --git a/x-pack/packages/kbn-entities-schema/src/schema/entity.ts b/x-pack/packages/kbn-entities-schema/src/schema/entity.ts new file mode 100644 index 0000000000000..514ed01494036 --- /dev/null +++ b/x-pack/packages/kbn-entities-schema/src/schema/entity.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { z } from 'zod'; +import { arrayOfStringsSchema } from './common'; + +export const entitySchema = z.intersection( + z.object({ + entity: z.object({ + id: z.string(), + indexPatterns: arrayOfStringsSchema, + identityFields: arrayOfStringsSchema, + metric: z.record(z.string(), z.number()), + }), + }), + z.record(z.string(), z.string().or(z.number())) +); diff --git a/x-pack/packages/kbn-entities-schema/src/schema/entity_definition.ts b/x-pack/packages/kbn-entities-schema/src/schema/entity_definition.ts new file mode 100644 index 0000000000000..48b8c8060efbc --- /dev/null +++ b/x-pack/packages/kbn-entities-schema/src/schema/entity_definition.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { z } from 'zod'; +import { + arrayOfStringsSchema, + entityTypeSchema, + keyMetricSchema, + metadataSchema, + filterSchema, + durationSchema, + identityFieldsSchema, +} from './common'; + +export const entityDefinitionSchema = z.object({ + id: z.string().regex(/^[\w-]+$/), + name: z.string(), + description: z.optional(z.string()), + type: entityTypeSchema, + filter: filterSchema, + indexPatterns: arrayOfStringsSchema, + identityFields: z.array(identityFieldsSchema), + identityTemplate: z.string(), + metadata: z.optional(z.array(metadataSchema)), + metrics: z.optional(z.array(keyMetricSchema)), + staticFields: z.optional(z.record(z.string(), z.string())), + lookback: durationSchema, + timestampField: z.string(), + settings: z.optional( + z.object({ + syncField: z.optional(z.string()), + syncDelay: z.optional(z.string()), + frequency: z.optional(z.string()), + }) + ), +}); + +export type EntityDefinition = z.infer; diff --git a/x-pack/packages/kbn-entities-schema/tsconfig.json b/x-pack/packages/kbn-entities-schema/tsconfig.json new file mode 100644 index 0000000000000..f722f3587e7a2 --- /dev/null +++ b/x-pack/packages/kbn-entities-schema/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts" + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + ] +} diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/tabs/incompatible_tab/index.tsx b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/tabs/incompatible_tab/index.tsx index e1b1bdfc9a403..aa6866555a3b3 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/tabs/incompatible_tab/index.tsx +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/tabs/incompatible_tab/index.tsx @@ -145,18 +145,17 @@ const IncompatibleTabComponent: React.FC = ({ - {isAssistantEnabled && ( - - - - )} + + + diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/tabs/summary_tab/callout_summary/index.tsx b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/tabs/summary_tab/callout_summary/index.tsx index d9403b7159e9f..aa32baafecf19 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/tabs/summary_tab/callout_summary/index.tsx +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/tabs/summary_tab/callout_summary/index.tsx @@ -138,18 +138,17 @@ const CalloutSummaryComponent: React.FC = ({ - {isAssistantEnabled && ( - - - - )} + + + diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/helpers.test.ts b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/helpers.test.ts index 48a83974ba94d..8cec06b28d3a3 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/helpers.test.ts +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/helpers.test.ts @@ -1483,10 +1483,9 @@ describe('helpers', () => { }); expect(fetch).toHaveBeenCalledWith( - '/internal/ecs_data_quality_dashboard/results', + '/internal/ecs_data_quality_dashboard/results/indices_latest/auditbeat-*', expect.objectContaining({ method: 'GET', - query: { pattern: 'auditbeat-*' }, }) ); }); diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/helpers.ts b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/helpers.ts index 368a1d62ac72e..c34f8e35ed289 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/helpers.ts +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/helpers.ts @@ -457,6 +457,8 @@ export const getErrorSummaries = ( }; export const RESULTS_API_ROUTE = '/internal/ecs_data_quality_dashboard/results'; +export const RESULTS_INDICES_LATEST_ROUTE = + '/internal/ecs_data_quality_dashboard/results/indices_latest/{pattern}'; export interface StorageResult { batchId: string; @@ -565,11 +567,11 @@ export async function getStorageResults({ abortController: AbortController; }): Promise { try { - const results = await httpFetch(RESULTS_API_ROUTE, { + const route = RESULTS_INDICES_LATEST_ROUTE.replace('{pattern}', pattern); + const results = await httpFetch(route, { method: 'GET', signal: abortController.signal, version: INTERNAL_API_VERSION, - query: { pattern }, }); return results; } catch (err) { diff --git a/x-pack/plugins/actions/server/index.ts b/x-pack/plugins/actions/server/index.ts index 39b94204520b7..ef1c277ff049b 100644 --- a/x-pack/plugins/actions/server/index.ts +++ b/x-pack/plugins/actions/server/index.ts @@ -33,6 +33,7 @@ export { asSavedObjectExecutionSource, asHttpRequestExecutionSource, asNotificationExecutionSource, + getBasicAuthHeader, } from './lib'; export { ACTION_SAVED_OBJECT_TYPE } from './constants/saved_objects'; diff --git a/x-pack/plugins/actions/server/lib/axios_utils.test.ts b/x-pack/plugins/actions/server/lib/axios_utils.test.ts index 43f16b4863e9a..c5e23f6cd3db3 100644 --- a/x-pack/plugins/actions/server/lib/axios_utils.test.ts +++ b/x-pack/plugins/actions/server/lib/axios_utils.test.ts @@ -339,6 +339,55 @@ describe('request', () => { `"Do not use \\"baseURL\\" in the creation of your axios instance because you will mostly break proxy"` ); }); + + test('it converts the auth property to basic auth header', async () => { + await request({ + axios, + url: '/test', + logger, + configurationUtilities, + auth: { username: 'username', password: 'password' }, + }); + + expect(axiosMock).toHaveBeenCalledWith('/test', { + method: 'get', + httpAgent: undefined, + httpsAgent: expect.any(HttpsAgent), + proxy: false, + maxContentLength: 1000000, + timeout: 360000, + headers: { Authorization: `Basic ${Buffer.from('username:password').toString('base64')}` }, + }); + }); + + test('it does not override an authorization header if provided', async () => { + await request({ + axios, + url: '/test', + logger, + configurationUtilities, + auth: { username: 'username', password: 'password' }, + headers: { + 'Content-Type': 'application/json', + 'X-Test-Header': 'test', + Authorization: 'Bearer my_token', + }, + }); + + expect(axiosMock).toHaveBeenCalledWith('/test', { + method: 'get', + httpAgent: undefined, + httpsAgent: expect.any(HttpsAgent), + proxy: false, + maxContentLength: 1000000, + timeout: 360000, + headers: { + 'Content-Type': 'application/json', + 'X-Test-Header': 'test', + Authorization: 'Bearer my_token', + }, + }); + }); }); describe('patch', () => { diff --git a/x-pack/plugins/actions/server/lib/axios_utils.ts b/x-pack/plugins/actions/server/lib/axios_utils.ts index b623f427be681..3852f2a33755b 100644 --- a/x-pack/plugins/actions/server/lib/axios_utils.ts +++ b/x-pack/plugins/actions/server/lib/axios_utils.ts @@ -18,6 +18,7 @@ import { Logger } from '@kbn/core/server'; import { getCustomAgents } from './get_custom_agents'; import { ActionsConfigurationUtilities } from '../actions_config'; import { SSLSettings } from '../types'; +import { combineHeadersWithBasicAuthHeader } from './get_basic_auth_header'; export const request = async ({ axios, @@ -55,10 +56,18 @@ export const request = async ({ const { maxContentLength, timeout: settingsTimeout } = configurationUtilities.getResponseSettings(); + const { auth, ...restConfig } = config; + + const headersWithBasicAuth = combineHeadersWithBasicAuthHeader({ + username: auth?.username, + password: auth?.password, + headers, + }); + return await axios(url, { - ...config, + ...restConfig, method, - headers, + headers: headersWithBasicAuth, ...(data ? { data } : {}), // use httpAgent and httpsAgent and set axios proxy: false, to be able to handle fail on invalid certs httpAgent, diff --git a/x-pack/plugins/actions/server/lib/get_basic_auth_header.test.ts b/x-pack/plugins/actions/server/lib/get_basic_auth_header.test.ts new file mode 100644 index 0000000000000..b5064797de38a --- /dev/null +++ b/x-pack/plugins/actions/server/lib/get_basic_auth_header.test.ts @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { combineHeadersWithBasicAuthHeader, getBasicAuthHeader } from './get_basic_auth_header'; + +describe('get_basic_auth_header', () => { + describe('getBasicAuthHeader', () => { + it('constructs the basic auth header correctly', () => { + expect(getBasicAuthHeader({ username: 'test', password: 'foo' })).toEqual({ + Authorization: `Basic ${Buffer.from('test:foo').toString('base64')}`, + }); + }); + }); + + describe('combineHeadersWithBasicAuthHeader', () => { + it('constructs the basic auth header correctly', () => { + expect(combineHeadersWithBasicAuthHeader({ username: 'test', password: 'foo' })).toEqual({ + Authorization: `Basic ${Buffer.from('test:foo').toString('base64')}`, + }); + }); + + it('adds extra headers correctly', () => { + expect( + combineHeadersWithBasicAuthHeader({ + username: 'test', + password: 'foo', + headers: { 'X-token': 'foo' }, + }) + ).toEqual({ + Authorization: `Basic ${Buffer.from('test:foo').toString('base64')}`, + 'X-token': 'foo', + }); + }); + + it('does not overrides the auth header if provided', () => { + expect( + combineHeadersWithBasicAuthHeader({ + username: 'test', + password: 'foo', + headers: { Authorization: 'Bearer my_token' }, + }) + ).toEqual({ + Authorization: 'Bearer my_token', + }); + }); + + it('returns only the headers if auth is undefined', () => { + expect( + combineHeadersWithBasicAuthHeader({ + headers: { 'X-token': 'foo' }, + }) + ).toEqual({ + 'X-token': 'foo', + }); + }); + + it('returns undefined with no arguments', () => { + expect(combineHeadersWithBasicAuthHeader()).toEqual(undefined); + }); + + it('returns undefined when headers are null', () => { + expect(combineHeadersWithBasicAuthHeader({ headers: null })).toEqual(undefined); + }); + }); +}); diff --git a/x-pack/plugins/actions/server/lib/get_basic_auth_header.ts b/x-pack/plugins/actions/server/lib/get_basic_auth_header.ts new file mode 100644 index 0000000000000..8408f6be76f2b --- /dev/null +++ b/x-pack/plugins/actions/server/lib/get_basic_auth_header.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { AxiosHeaderValue } from 'axios'; + +interface GetBasicAuthHeaderArgs { + username: string; + password: string; +} + +type CombineHeadersWithBasicAuthHeader = Partial & { + headers?: Record | null; +}; + +export const getBasicAuthHeader = ({ username, password }: GetBasicAuthHeaderArgs) => { + const header = `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`; + + return { Authorization: header }; +}; + +export const combineHeadersWithBasicAuthHeader = ({ + username, + password, + headers, +}: CombineHeadersWithBasicAuthHeader = {}) => { + return username != null && password != null + ? { ...getBasicAuthHeader({ username, password }), ...headers } + : headers ?? undefined; +}; diff --git a/x-pack/plugins/actions/server/lib/index.ts b/x-pack/plugins/actions/server/lib/index.ts index e97198ee7b432..2737d83abfff6 100644 --- a/x-pack/plugins/actions/server/lib/index.ts +++ b/x-pack/plugins/actions/server/lib/index.ts @@ -38,3 +38,4 @@ export { export { validateEmptyStrings } from './validate_empty_strings'; export { parseDate } from './parse_date'; export type { RelatedSavedObjects } from './related_saved_objects'; +export { getBasicAuthHeader, combineHeadersWithBasicAuthHeader } from './get_basic_auth_header'; diff --git a/x-pack/plugins/actions/server/sub_action_framework/mocks.ts b/x-pack/plugins/actions/server/sub_action_framework/mocks.ts index 572aa20422a94..f6c8e86dd5af3 100644 --- a/x-pack/plugins/actions/server/sub_action_framework/mocks.ts +++ b/x-pack/plugins/actions/server/sub_action_framework/mocks.ts @@ -78,6 +78,18 @@ export class TestSubActionConnector extends SubActionConnector } = {}) { + const res = await this.request({ + url: 'https://example.com', + data: {}, + auth: { username: 'username', password: 'password' }, + headers: { 'X-Test-Header': 'test', ...headers }, + responseSchema: schema.object({ status: schema.string() }), + }); + + return res; + } } export class TestNoSubActions extends SubActionConnector { diff --git a/x-pack/plugins/actions/server/sub_action_framework/sub_action_connector.test.ts b/x-pack/plugins/actions/server/sub_action_framework/sub_action_connector.test.ts index 5980072217dd5..1358684d86093 100644 --- a/x-pack/plugins/actions/server/sub_action_framework/sub_action_connector.test.ts +++ b/x-pack/plugins/actions/server/sub_action_framework/sub_action_connector.test.ts @@ -210,5 +210,43 @@ describe('SubActionConnector', () => { 'Request to external service failed. Connector Id: test-id. Connector type: .test. Method: get. URL: https://example.com' ); }); + + it('converts auth axios property to a basic auth header if provided', async () => { + await service.testAuth(); + + expect(requestMock).toHaveBeenCalledTimes(1); + expect(requestMock).toBeCalledWith({ + axios: axiosInstanceMock, + configurationUtilities: mockedActionsConfig, + logger, + method: 'get', + data: {}, + headers: { + 'Content-Type': 'application/json', + 'X-Test-Header': 'test', + Authorization: `Basic ${Buffer.from('username:password').toString('base64')}`, + }, + url: 'https://example.com', + }); + }); + + it('does not override an authorization header if provided', async () => { + await service.testAuth({ headers: { Authorization: 'Bearer my_token' } }); + + expect(requestMock).toHaveBeenCalledTimes(1); + expect(requestMock).toBeCalledWith({ + axios: axiosInstanceMock, + configurationUtilities: mockedActionsConfig, + logger, + method: 'get', + data: {}, + headers: { + 'Content-Type': 'application/json', + 'X-Test-Header': 'test', + Authorization: 'Bearer my_token', + }, + url: 'https://example.com', + }); + }); }); }); diff --git a/x-pack/plugins/actions/server/sub_action_framework/sub_action_connector.ts b/x-pack/plugins/actions/server/sub_action_framework/sub_action_connector.ts index 14e2ded5d1a38..19cc7e90d6254 100644 --- a/x-pack/plugins/actions/server/sub_action_framework/sub_action_connector.ts +++ b/x-pack/plugins/actions/server/sub_action_framework/sub_action_connector.ts @@ -15,6 +15,7 @@ import axios, { AxiosRequestHeaders, AxiosHeaders, AxiosHeaderValue, + AxiosBasicCredentials, } from 'axios'; import { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; @@ -29,6 +30,7 @@ import { SubAction, SubActionRequestParams } from './types'; import { ServiceParams } from './types'; import * as i18n from './translations'; import { request } from '../lib/axios_utils'; +import { combineHeadersWithBasicAuthHeader } from '../lib/get_basic_auth_header'; const isObject = (value: unknown): value is Record => { return isPlainObject(value); @@ -87,8 +89,17 @@ export abstract class SubActionConnector { } } - private getHeaders(headers?: AxiosRequestHeaders): Record { - return { 'Content-Type': 'application/json', ...headers }; + private getHeaders( + auth?: AxiosBasicCredentials, + headers?: AxiosRequestHeaders + ): Record { + const headersWithBasicAuth = combineHeadersWithBasicAuthHeader({ + username: auth?.username, + password: auth?.password, + headers, + }); + + return { 'Content-Type': 'application/json', ...headersWithBasicAuth }; } private validateResponse(responseSchema: Type, data: unknown) { @@ -137,15 +148,17 @@ export abstract class SubActionConnector { `Request to external service. Connector Id: ${this.connector.id}. Connector type: ${this.connector.type} Method: ${method}. URL: ${normalizedURL}` ); + const { auth, ...restConfig } = config; + const res = await request({ - ...config, + ...restConfig, axios: this.axiosInstance, url: normalizedURL, logger: this.logger, method, data: this.normalizeData(data), configurationUtilities: this.configurationUtilities, - headers: this.getHeaders(headers as AxiosHeaders), + headers: this.getHeaders(auth, headers as AxiosHeaders), timeout, }); diff --git a/x-pack/plugins/alerting/server/lib/get_alerts_for_notification.test.ts b/x-pack/plugins/alerting/server/lib/get_alerts_for_notification.test.ts index 2528a27f19f9e..b50025b415178 100644 --- a/x-pack/plugins/alerting/server/lib/get_alerts_for_notification.test.ts +++ b/x-pack/plugins/alerting/server/lib/get_alerts_for_notification.test.ts @@ -674,6 +674,36 @@ describe('getAlertsForNotification', () => { expect(delayedAlertsCount).toBe(2); }); + test('should remove the alert from recoveredAlerts and should not return the alert in currentRecoveredAlerts if the activeCount is less than the rule alertDelay', () => { + const alert1 = new Alert('1', { + meta: { activeCount: 1, uuid: 'uuid-1' }, + }); + const alert2 = new Alert('2', { meta: { uuid: 'uuid-2' } }); + + const { recoveredAlerts, currentRecoveredAlerts, delayedAlertsCount } = + getAlertsForNotification( + DEFAULT_FLAPPING_SETTINGS, + true, + 'default', + 5, + {}, + {}, + { + // recovered alerts + '1': alert1, + '2': alert2, + }, + { + // current recovered alerts + '1': alert1, + '2': alert2, + } + ); + expect(recoveredAlerts).toMatchInlineSnapshot(`Object {}`); + expect(currentRecoveredAlerts).toMatchInlineSnapshot(`Object {}`); + expect(delayedAlertsCount).toBe(0); + }); + test('should update active alert to look like a new alert if the activeCount is equal to the rule alertDelay', () => { const alert2 = new Alert('2', { meta: { uuid: 'uuid-2' } }); diff --git a/x-pack/plugins/alerting/server/lib/get_alerts_for_notification.ts b/x-pack/plugins/alerting/server/lib/get_alerts_for_notification.ts index c5c7ac017b2c1..c1974810b46d0 100644 --- a/x-pack/plugins/alerting/server/lib/get_alerts_for_notification.ts +++ b/x-pack/plugins/alerting/server/lib/get_alerts_for_notification.ts @@ -54,6 +54,12 @@ export function getAlertsForNotification< for (const id of keys(currentRecoveredAlerts)) { const alert = recoveredAlerts[id]; + // if alert has not reached the alertDelay threshold don't recover the alert + if (alert.getActiveCount() < alertDelay) { + // remove from recovered alerts + delete recoveredAlerts[id]; + delete currentRecoveredAlerts[id]; + } alert.resetActiveCount(); if (flappingSettings.enabled) { const flapping = alert.getFlapping(); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx index 696243680782b..fd317d09ddfd9 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx @@ -14,14 +14,11 @@ import { isErrorEmbeddable, ReactEmbeddableRenderer, } from '@kbn/embeddable-plugin/public'; -import { PresentationContainer } from '@kbn/presentation-containers'; -import { EmbeddableAppContext } from '@kbn/presentation-publishing'; import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; -import React, { FC } from 'react'; +import React, { FC, useMemo } from 'react'; import ReactDOM from 'react-dom'; -import useObservable from 'react-use/lib/useObservable'; import { pluginServices } from '../../../public/services'; -import { CANVAS_APP, CANVAS_EMBEDDABLE_CLASSNAME } from '../../../common/lib'; +import { CANVAS_EMBEDDABLE_CLASSNAME } from '../../../common/lib'; import { RendererStrings } from '../../../i18n'; import { CanvasContainerApi, @@ -32,6 +29,7 @@ import { import { EmbeddableExpression } from '../../expression_types/embeddable'; import { StartDeps } from '../../plugin'; import { embeddableInputToExpression } from './embeddable_input_to_expression'; +import { useGetAppContext } from './use_get_app_context'; const { embeddable: strings } = RendererStrings; @@ -55,28 +53,41 @@ const renderReactEmbeddable = ({ handlers: RendererHandlers; core: CoreStart; }) => { + // wrap in functional component to allow usage of hooks + const RendererWrapper: FC<{ canvasApi: CanvasContainerApi }> = ({ canvasApi }) => { + const getAppContext = useGetAppContext(core); + + useMemo(() => { + canvasApi.getAppContext = getAppContext; + }, [canvasApi, getAppContext]); + + return ( + { + const newExpression = embeddableInputToExpression( + newState.rawState as unknown as EmbeddableInput, + type, + undefined, + true + ); + if (newExpression) handlers.onEmbeddableInputChange(newExpression); + }} + /> + ); + }; + return (
- { - const newExpression = embeddableInputToExpression( - newState.rawState as unknown as EmbeddableInput, - type, - undefined, - true - ); - if (newExpression) handlers.onEmbeddableInputChange(newExpression); - }} - /> +
); @@ -84,23 +95,9 @@ const renderReactEmbeddable = ({ const renderEmbeddableFactory = (core: CoreStart, _plugins: StartDeps) => { const EmbeddableRenderer: FC<{ embeddable: IEmbeddable }> = ({ embeddable }) => { - const currentAppId = useObservable(core.application.currentAppId$, undefined); - - if (!currentAppId) { - return null; - } - - const canvasAppContext: EmbeddableAppContext = { - getCurrentPath: () => { - const urlToApp = core.application.getUrlForApp(currentAppId); - const inAppPath = window.location.pathname.replace(urlToApp, ''); - - return inAppPath + window.location.search + window.location.hash; - }, - currentAppId: CANVAS_APP, - }; + const getAppContext = useGetAppContext(core); - embeddable.getAppContext = () => canvasAppContext; + embeddable.getAppContext = getAppContext; return ; }; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/use_get_app_context.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/use_get_app_context.tsx new file mode 100644 index 0000000000000..7054d82582184 --- /dev/null +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/use_get_app_context.tsx @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useMemo } from 'react'; +import type { CoreStart } from '@kbn/core/public'; +import useObservable from 'react-use/lib/useObservable'; +import { CANVAS_APP } from '../../../common/lib'; + +export function useGetAppContext(core: CoreStart) { + const currentAppId = useObservable(core.application.currentAppId$, undefined); + const getAppContext = useMemo(() => { + return () => ({ + getCurrentPath: () => { + const urlToApp = core.application.getUrlForApp(currentAppId ?? CANVAS_APP); + const inAppPath = window.location.pathname.replace(urlToApp, ''); + + return inAppPath + window.location.search + window.location.hash; + }, + currentAppId: CANVAS_APP, + }); + }, [currentAppId, core.application]); + return getAppContext; +} diff --git a/x-pack/plugins/canvas/types/embeddables.ts b/x-pack/plugins/canvas/types/embeddables.ts index 514cbea6c6773..38aae5f33be7b 100644 --- a/x-pack/plugins/canvas/types/embeddables.ts +++ b/x-pack/plugins/canvas/types/embeddables.ts @@ -8,7 +8,7 @@ import type { TimeRange } from '@kbn/es-query'; import { Filter } from '@kbn/es-query'; import { EmbeddableInput as Input } from '@kbn/embeddable-plugin/common'; -import { PublishesViewMode } from '@kbn/presentation-publishing'; +import { HasAppContext, PublishesViewMode } from '@kbn/presentation-publishing'; import { CanAddNewPanel } from '@kbn/presentation-containers'; export type EmbeddableInput = Input & { @@ -17,4 +17,4 @@ export type EmbeddableInput = Input & { savedObjectId?: string; }; -export type CanvasContainerApi = PublishesViewMode & CanAddNewPanel; +export type CanvasContainerApi = PublishesViewMode & CanAddNewPanel & Partial; diff --git a/x-pack/plugins/cases/public/components/connectors/resilient/use_get_incident_types.test.tsx b/x-pack/plugins/cases/public/components/connectors/resilient/use_get_incident_types.test.tsx index 7bff41383b77a..569483e43e566 100644 --- a/x-pack/plugins/cases/public/components/connectors/resilient/use_get_incident_types.test.tsx +++ b/x-pack/plugins/cases/public/components/connectors/resilient/use_get_incident_types.test.tsx @@ -19,19 +19,18 @@ jest.mock('./api'); const useKibanaMock = useKibana as jest.Mocked; -// FLAKY: https://github.com/elastic/kibana/issues/182845 -describe.skip('useGetIncidentTypes', () => { +describe('useGetIncidentTypes', () => { const { http } = useKibanaMock().services; let appMockRender: AppMockRenderer; beforeEach(() => { - appMockRender = createAppMockRenderer(); jest.clearAllMocks(); + appMockRender = createAppMockRenderer(); }); it('calls the api when invoked with the correct parameters', async () => { const spy = jest.spyOn(api, 'getIncidentTypes'); - const { waitForNextUpdate } = renderHook( + const { waitFor } = renderHook( () => useGetIncidentTypes({ http, @@ -40,7 +39,9 @@ describe.skip('useGetIncidentTypes', () => { { wrapper: appMockRender.AppWrapper } ); - await waitForNextUpdate(); + await waitFor(() => { + expect(spy).toHaveBeenCalled(); + }); expect(spy).toHaveBeenCalledWith({ http, @@ -71,7 +72,7 @@ describe.skip('useGetIncidentTypes', () => { const addError = jest.fn(); (useToasts as jest.Mock).mockReturnValue({ addSuccess: jest.fn(), addError }); - const { waitForNextUpdate } = renderHook( + const { waitFor } = renderHook( () => useGetIncidentTypes({ http, @@ -80,8 +81,9 @@ describe.skip('useGetIncidentTypes', () => { { wrapper: appMockRender.AppWrapper } ); - await waitForNextUpdate(); - expect(addError).toHaveBeenCalled(); + await waitFor(() => { + expect(addError).toHaveBeenCalled(); + }); }); it('calls addError when the getIncidentTypes api returns successfully but contains an error', async () => { @@ -95,7 +97,7 @@ describe.skip('useGetIncidentTypes', () => { const addError = jest.fn(); (useToasts as jest.Mock).mockReturnValue({ addSuccess: jest.fn(), addError }); - const { waitForNextUpdate } = renderHook( + const { waitFor } = renderHook( () => useGetIncidentTypes({ http, @@ -104,7 +106,8 @@ describe.skip('useGetIncidentTypes', () => { { wrapper: appMockRender.AppWrapper } ); - await waitForNextUpdate(); - expect(addError).toHaveBeenCalled(); + await waitFor(() => { + expect(addError).toHaveBeenCalled(); + }); }); }); diff --git a/x-pack/plugins/cases/public/containers/user_profiles/use_get_current_user_profile.test.ts b/x-pack/plugins/cases/public/containers/user_profiles/use_get_current_user_profile.test.ts index cbc39d183d870..c26a6af826548 100644 --- a/x-pack/plugins/cases/public/containers/user_profiles/use_get_current_user_profile.test.ts +++ b/x-pack/plugins/cases/public/containers/user_profiles/use_get_current_user_profile.test.ts @@ -18,16 +18,16 @@ jest.mock('./api'); const useKibanaMock = useKibana as jest.Mock; -// FLAKY: https://github.com/elastic/kibana/issues/183144 -describe.skip('useGetCurrentUserProfile', () => { +describe('useGetCurrentUserProfile', () => { const addSuccess = jest.fn(); (useToasts as jest.Mock).mockReturnValue({ addSuccess, addError: jest.fn() }); let appMockRender: AppMockRenderer; beforeEach(() => { - appMockRender = createAppMockRenderer(); jest.clearAllMocks(); + + appMockRender = createAppMockRenderer(); useKibanaMock.mockReturnValue({ services: { ...createStartServicesMock() }, }); @@ -36,11 +36,13 @@ describe.skip('useGetCurrentUserProfile', () => { it('calls getCurrentUserProfile with correct arguments', async () => { const spyOnGetCurrentUserProfile = jest.spyOn(api, 'getCurrentUserProfile'); - const { result, waitFor } = renderHook(() => useGetCurrentUserProfile(), { + const { waitFor } = renderHook(() => useGetCurrentUserProfile(), { wrapper: appMockRender.AppWrapper, }); - await waitFor(() => result.current.isSuccess); + await waitFor(() => { + expect(spyOnGetCurrentUserProfile).toBeCalled(); + }); expect(spyOnGetCurrentUserProfile).toBeCalledWith({ security: expect.anything(), @@ -57,13 +59,13 @@ describe.skip('useGetCurrentUserProfile', () => { const addError = jest.fn(); (useToasts as jest.Mock).mockReturnValue({ addSuccess, addError }); - const { result, waitFor } = renderHook(() => useGetCurrentUserProfile(), { + const { waitFor } = renderHook(() => useGetCurrentUserProfile(), { wrapper: appMockRender.AppWrapper, }); - await waitFor(() => result.current.isError); - - expect(addError).toHaveBeenCalled(); + await waitFor(() => { + expect(addError).toHaveBeenCalled(); + }); }); it('does not show a toast error message when a 404 error is returned', async () => { @@ -76,13 +78,13 @@ describe.skip('useGetCurrentUserProfile', () => { const addError = jest.fn(); (useToasts as jest.Mock).mockReturnValue({ addSuccess, addError }); - const { result, waitFor } = renderHook(() => useGetCurrentUserProfile(), { + const { waitFor } = renderHook(() => useGetCurrentUserProfile(), { wrapper: appMockRender.AppWrapper, }); - await waitFor(() => result.current.isError); - - expect(addError).not.toHaveBeenCalled(); + await waitFor(() => { + expect(addError).not.toHaveBeenCalled(); + }); }); }); diff --git a/x-pack/plugins/cloud_integrations/cloud_links/public/plugin.test.ts b/x-pack/plugins/cloud_integrations/cloud_links/public/plugin.test.ts index d2f987337a440..d930d024d2484 100644 --- a/x-pack/plugins/cloud_integrations/cloud_links/public/plugin.test.ts +++ b/x-pack/plugins/cloud_integrations/cloud_links/public/plugin.test.ts @@ -26,7 +26,7 @@ describe('Cloud Links Plugin - public', () => { describe('start', () => { beforeEach(() => { - plugin.setup(); + plugin.setup(coreMock.createSetup()); }); afterEach(() => { 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 0e8b5eec26f65..9f385500b13e8 100755 --- a/x-pack/plugins/cloud_integrations/cloud_links/public/plugin.tsx +++ b/x-pack/plugins/cloud_integrations/cloud_links/public/plugin.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { CoreStart, Plugin } from '@kbn/core/public'; +import type { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; import type { CloudSetup, CloudStart } from '@kbn/cloud-plugin/public'; import type { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin/public'; import type { GuidedOnboardingPluginStart } from '@kbn/guided-onboarding-plugin/public'; @@ -30,7 +30,68 @@ interface CloudLinksDepsStart { export class CloudLinksPlugin implements Plugin { - public setup() {} + public setup({ analytics }: CoreSetup) { + analytics.registerEventType({ + eventType: 'connection_details_learn_more_clicked', + schema: {}, + }); + analytics.registerEventType({ + eventType: 'connection_details_tab_switched', + schema: { + tab: { + type: 'keyword', + _meta: { + description: 'Connection details tab that was switched to.', + optional: false, + }, + }, + }, + }); + analytics.registerEventType({ + eventType: 'connection_details_copy_endpoint_url_clicked', + schema: {}, + }); + analytics.registerEventType({ + eventType: 'connection_details_show_cloud_id_toggled', + schema: {}, + }); + analytics.registerEventType({ + eventType: 'connection_details_copy_cloud_id_clicked', + schema: {}, + }); + analytics.registerEventType({ + eventType: 'connection_details_new_api_key_created', + schema: {}, + }); + analytics.registerEventType({ + eventType: 'connection_details_manage_api_keys_clicked', + schema: {}, + }); + analytics.registerEventType({ + eventType: 'connection_details_key_encoding_changed', + schema: { + format: { + type: 'keyword', + _meta: { + description: 'The format of the API key that was changed to.', + optional: false, + }, + }, + }, + }); + analytics.registerEventType({ + eventType: 'connection_details_copy_api_key_clicked', + schema: { + format: { + type: 'keyword', + _meta: { + description: 'The format of the API key that was copied.', + optional: false, + }, + }, + }, + }); + } public start(core: CoreStart, plugins: CloudLinksDepsStart) { const { cloud, security, guidedOnboarding, share } = plugins; diff --git a/x-pack/plugins/ecs_data_quality_dashboard/common/constants.ts b/x-pack/plugins/ecs_data_quality_dashboard/common/constants.ts index d979d22b12b88..7c35cd9b2fbbd 100755 --- a/x-pack/plugins/ecs_data_quality_dashboard/common/constants.ts +++ b/x-pack/plugins/ecs_data_quality_dashboard/common/constants.ts @@ -14,4 +14,5 @@ export const GET_INDEX_MAPPINGS = `${BASE_PATH}/mappings/{pattern}`; export const GET_UNALLOWED_FIELD_VALUES = `${BASE_PATH}/unallowed_field_values`; export const GET_ILM_EXPLAIN = `${BASE_PATH}/ilm_explain/{pattern}`; export const RESULTS_ROUTE_PATH = `${BASE_PATH}/results`; +export const RESULTS_INDICES_LATEST_ROUTE_PATH = `${BASE_PATH}/results/indices_latest/{pattern}`; export const INTERNAL_API_VERSION = '1'; diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results.test.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results_indices_latest.test.ts similarity index 91% rename from x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results.test.ts rename to x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results_indices_latest.test.ts index 05a714a27275a..7aad4c347a8cb 100644 --- a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results.test.ts +++ b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results_indices_latest.test.ts @@ -4,13 +4,13 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { RESULTS_ROUTE_PATH } from '../../../common/constants'; +import { RESULTS_INDICES_LATEST_ROUTE_PATH } from '../../../common/constants'; import { serverMock } from '../../__mocks__/server'; import { requestMock } from '../../__mocks__/request'; import { requestContextMock } from '../../__mocks__/request_context'; -import type { LatestAggResponseBucket } from './get_results'; -import { getResultsRoute, getQuery } from './get_results'; +import type { LatestAggResponseBucket } from './get_results_indices_latest'; +import { getResultsIndicesLatestRoute, getQuery } from './get_results_indices_latest'; import { loggerMock, type MockedLogger } from '@kbn/logging-mocks'; import { resultDocument } from './results.mock'; import type { SearchResponse } from '@elastic/elasticsearch/lib/api/types'; @@ -41,7 +41,7 @@ jest.mock('./privileges', () => ({ mockCheckIndicesPrivileges(params), })); -describe('getResultsRoute route', () => { +describe('getResultsIndicesLatestRoute route', () => { describe('querying', () => { let server: ReturnType; let { context } = requestContextMock.createTools(); @@ -49,8 +49,8 @@ describe('getResultsRoute route', () => { const req = requestMock.create({ method: 'get', - path: RESULTS_ROUTE_PATH, - query: { pattern: 'logs-*' }, + path: RESULTS_INDICES_LATEST_ROUTE_PATH, + params: { pattern: 'logs-*' }, }); beforeEach(() => { @@ -65,7 +65,7 @@ describe('getResultsRoute route', () => { [resultDocument.indexName]: {}, }); - getResultsRoute(server.router, logger); + getResultsIndicesLatestRoute(server.router, logger); }); it('gets result', async () => { @@ -114,8 +114,8 @@ describe('getResultsRoute route', () => { const req = requestMock.create({ method: 'get', - path: RESULTS_ROUTE_PATH, - query: { pattern: 'logs-*' }, + path: RESULTS_INDICES_LATEST_ROUTE_PATH, + params: { pattern: 'logs-*' }, }); beforeEach(() => { @@ -132,7 +132,7 @@ describe('getResultsRoute route', () => { [resultDocument.indexName]: {}, }); - getResultsRoute(server.router, logger); + getResultsIndicesLatestRoute(server.router, logger); }); it('should authorize indices from pattern', async () => { @@ -225,14 +225,14 @@ describe('getResultsRoute route', () => { beforeEach(() => { server = serverMock.create(); logger = loggerMock.create(); - getResultsRoute(server.router, logger); + getResultsIndicesLatestRoute(server.router, logger); }); - test('disallows invalid query param', () => { + test('disallows invalid path param', () => { const req = requestMock.create({ method: 'get', - path: RESULTS_ROUTE_PATH, - query: {}, + path: RESULTS_INDICES_LATEST_ROUTE_PATH, + params: {}, }); const result = server.validate(req); diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results_indices_latest.ts similarity index 92% rename from x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results.ts rename to x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results_indices_latest.ts index 6c410e88f3626..a4508e1471b3b 100644 --- a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results.ts +++ b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results_indices_latest.ts @@ -7,10 +7,10 @@ import type { IRouter, Logger } from '@kbn/core/server'; -import { RESULTS_ROUTE_PATH, INTERNAL_API_VERSION } from '../../../common/constants'; +import { INTERNAL_API_VERSION, RESULTS_INDICES_LATEST_ROUTE_PATH } from '../../../common/constants'; import { buildResponse } from '../../lib/build_response'; import { buildRouteValidation } from '../../schemas/common'; -import { GetResultQuery } from '../../schemas/result'; +import { GetResultParams } from '../../schemas/result'; import type { ResultDocument } from '../../schemas/result'; import { API_DEFAULT_ERROR_MESSAGE } from '../../translations'; import type { DataQualityDashboardRequestHandlerContext } from '../../types'; @@ -33,20 +33,24 @@ export interface LatestAggResponseBucket { latest_doc: { hits: { hits: Array<{ _source: ResultDocument }> } }; } -export const getResultsRoute = ( +export const getResultsIndicesLatestRoute = ( router: IRouter, logger: Logger ) => { router.versioned .get({ - path: RESULTS_ROUTE_PATH, + path: RESULTS_INDICES_LATEST_ROUTE_PATH, access: 'internal', options: { tags: ['access:securitySolution'] }, }) .addVersion( { version: INTERNAL_API_VERSION, - validate: { request: { query: buildRouteValidation(GetResultQuery) } }, + validate: { + request: { + params: buildRouteValidation(GetResultParams), + }, + }, }, async (context, request, response) => { const services = await context.resolve(['core', 'dataQualityDashboard']); @@ -65,7 +69,7 @@ export const getResultsRoute = ( try { const { client } = services.core.elasticsearch; - const { pattern } = request.query; + const { pattern } = request.params; // Discover all indices for the pattern using internal user const indicesResponse = await client.asInternalUser.indices.get({ diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/index.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/index.ts index 25d4a913a1946..d403ece283b1d 100644 --- a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/index.ts +++ b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/index.ts @@ -8,7 +8,7 @@ import type { IRouter, Logger } from '@kbn/core/server'; import { postResultsRoute } from './post_results'; -import { getResultsRoute } from './get_results'; +import { getResultsIndicesLatestRoute } from './get_results_indices_latest'; import type { DataQualityDashboardRequestHandlerContext } from '../../types'; export const resultsRoutes = ( @@ -16,5 +16,5 @@ export const resultsRoutes = ( logger: Logger ) => { postResultsRoute(router, logger); - getResultsRoute(router, logger); + getResultsIndicesLatestRoute(router, logger); }; diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/schemas/result.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/schemas/result.ts index 9e8b7540bf2e5..055930d82cefb 100644 --- a/x-pack/plugins/ecs_data_quality_dashboard/server/schemas/result.ts +++ b/x-pack/plugins/ecs_data_quality_dashboard/server/schemas/result.ts @@ -39,5 +39,5 @@ export type ResultDocument = t.TypeOf; export const PostResultBody = ResultDocument; -export const GetResultQuery = t.type({ pattern: t.string }); -export type GetResultQuery = t.TypeOf; +export const GetResultParams = t.type({ pattern: t.string }); +export type GetResultParams = t.TypeOf; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/add_data_panel_content.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/add_data_panel_content.tsx index 5d2d8cecf8466..55420cc17895b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/add_data_panel_content.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/add_data_panel_content.tsx @@ -38,7 +38,7 @@ export const AddDataPanelContent: React.FC = ({ > = ({ children }) => { - const [isPopoverOpen, setPopoverOpen] = useState(false); - const { cloud, esConfig, navigateToUrl } = useValues(KibanaLogic); - const { makeRequest } = useActions(FetchApiKeysAPILogic); - const { data } = useValues(FetchApiKeysAPILogic); - const [isFlyoutOpen, setIsFlyoutOpen] = useState(false); - const { euiTheme } = useEuiTheme(); - - useEffect(() => makeRequest({}), []); - - const COPIED_LABEL = i18n.translate('xpack.enterpriseSearch.pageTemplate.apiKey.copied', { - defaultMessage: 'Copied', - }); - - const apiKeys = data?.api_keys || []; - const cloudId = cloud?.cloudId; - const elasticsearchEndpoint = esConfig.elasticsearch_host; - - const button = ( - setPopoverOpen(!isPopoverOpen)}> - {i18n.translate('xpack.enterpriseSearch.pageTemplate.endpointsButtonLabel', { - defaultMessage: 'Endpoints & API keys', - })} - - ); +export const EndpointsHeaderAction: React.FC = ({ children }) => { + const [open, setOpen] = React.useState(false); return ( - - - {Boolean(children) && {children}} - - {isFlyoutOpen && setIsFlyoutOpen(false)} />} - setPopoverOpen(false)} - panelPaddingSize="none" - anchorPosition="downLeft" + <> + + + {!!children && {children}} + setOpen((x) => !x)} + data-test-subj="enterpriseSearchEndpointsHeaderActionEndpointsApiKeysButton" > - - - {i18n.translate( - 'xpack.enterpriseSearch.pageTemplate.apiKey.elasticsearchEndpoint', - { - defaultMessage: 'Elasticsearch endpoint:', - } - )} - - - - - - - {elasticsearchEndpoint} - - - - - {(copy) => ( - - )} - - - - , - ...(Boolean(cloudId) - ? [ - - - {i18n.translate('xpack.enterpriseSearch.apiKey.cloudId', { - defaultMessage: 'Cloud ID:', - })} - - - - - - - {cloudId} - - - - - {(copy) => ( - - )} - - - - , - ] - : []), - - - - - 0 ? 'success' : 'warning'} - data-test-subj="api-keys-count-badge" - > - {apiKeys.length} - - ), - }} - /> - - - - - navigateToUrl('/app/management/security/api_keys', { - shouldNotCreateHref: true, - }) - } - /> - - - , - , - - { - setIsFlyoutOpen(true); - setPopoverOpen(false); - }} - data-test-subj="new-api-key-button" - fullWidth - > - - {i18n.translate('xpack.enterpriseSearch.pageTemplate.apiKey.newButtonLabel', { - defaultMessage: 'New API key', - })} - - - , - ]} - /> - - - - + {i18n.translate('xpack.enterpriseSearch.pageTemplate.endpointsButtonLabel', { + defaultMessage: 'Endpoints & API keys', + })} + + + + {open && ( + setOpen(false)} size={'s'}> + + + + + )} + ); }; diff --git a/x-pack/plugins/enterprise_search/tsconfig.json b/x-pack/plugins/enterprise_search/tsconfig.json index 6161461b48b89..56232cd73f256 100644 --- a/x-pack/plugins/enterprise_search/tsconfig.json +++ b/x-pack/plugins/enterprise_search/tsconfig.json @@ -75,6 +75,7 @@ "@kbn/deeplinks-search", "@kbn/react-kibana-context-theme", "@kbn/search-types", + "@kbn/cloud", "@kbn/try-in-console" ] } diff --git a/x-pack/plugins/fleet/common/settings/agent_policy_settings.tsx b/x-pack/plugins/fleet/common/settings/agent_policy_settings.tsx index 4ffe46b680764..ed23ea121eae6 100644 --- a/x-pack/plugins/fleet/common/settings/agent_policy_settings.tsx +++ b/x-pack/plugins/fleet/common/settings/agent_policy_settings.tsx @@ -36,7 +36,7 @@ export const AGENT_POLICY_ADVANCED_SETTINGS: SettingsConfig[] = [ defaultMessage: 'Limits the maximum number of CPUs that can be executing simultaneously.', }), learnMoreLink: - 'https://www.elastic.co/guide/en/fleet/current/enable-custom-policy-settings.html#limit-cpu-usage', + 'https://www.elastic.co/guide/en/fleet/current/agent-policy.html#agent-policy-limit-cpu', api_field: { name: 'agent_limits_go_max_procs', }, @@ -121,7 +121,7 @@ export const AGENT_POLICY_ADVANCED_SETTINGS: SettingsConfig[] = [ } ), learnMoreLink: - 'https://www.elastic.co/guide/en/fleet/current/enable-custom-policy-settings.html#override-default-monitoring-port', + 'https://www.elastic.co/guide/en/fleet/current/agent-policy.html#agent-policy-http-monitoring', schema: z .object({ // enabled: z.boolean().describe('Enabled').default(false), @@ -146,6 +146,8 @@ export const AGENT_POLICY_ADVANCED_SETTINGS: SettingsConfig[] = [ api_field: { name: 'agent_logging_level', }, + learnMoreLink: + 'https://www.elastic.co/guide/en/fleet/current/agent-policy.html#agent-policy-log-level', schema: z.nativeEnum(AGENT_LOG_LEVELS).default(DEFAULT_LOG_LEVEL), }, ]; diff --git a/x-pack/plugins/fleet/server/collectors/agent_collectors.ts b/x-pack/plugins/fleet/server/collectors/agent_collectors.ts index 391114aa992b3..ad23bea85d365 100644 --- a/x-pack/plugins/fleet/server/collectors/agent_collectors.ts +++ b/x-pack/plugins/fleet/server/collectors/agent_collectors.ts @@ -69,6 +69,10 @@ export interface AgentData { error: number; degraded: number; }; + agents_per_privileges: { + root: number; + unprivileged: number; + }; agents_per_policy: number[]; agents_per_os: Array<{ name: string; @@ -89,6 +93,10 @@ const DEFAULT_AGENT_DATA = { agents_per_version: [], agents_per_os: [], upgrade_details: [], + agents_per_privileges: { + root: 0, + unprivileged: 0, + }, }; export const getAgentData = async ( @@ -159,6 +167,18 @@ export const getAgentData = async ( ], }, }, + privileges: { + filters: { + other_bucket_key: 'root', + filters: { + unprivileged: { + match: { + 'local_metadata.elastic.agent.unprivileged': true, + }, + }, + }, + }, + }, }, }, { signal: abortController.signal } @@ -176,6 +196,12 @@ export const getAgentData = async ( }) ); + const agentsPerPrivileges = { + root: (response?.aggregations?.privileges as any)?.buckets?.root?.doc_count ?? 0, + unprivileged: + (response?.aggregations?.privileges as any)?.buckets?.unprivileged?.doc_count ?? 0, + }; + const getAgentStatusesPerVersion = async (version: string) => { return await getAgentStatusForAgentPolicy( esClient, @@ -227,6 +253,7 @@ export const getAgentData = async ( agent_checkin_status: statuses, agents_per_policy: agentsPerPolicy, agents_per_version: agentsPerVersion, + agents_per_privileges: agentsPerPrivileges, agents_per_os: agentsPerOS, upgrade_details: upgradeDetails, }; diff --git a/x-pack/plugins/fleet/server/integration_tests/fleet_usage_telemetry.test.ts b/x-pack/plugins/fleet/server/integration_tests/fleet_usage_telemetry.test.ts index 430dc6f745ad9..dbce3c7f8b435 100644 --- a/x-pack/plugins/fleet/server/integration_tests/fleet_usage_telemetry.test.ts +++ b/x-pack/plugins/fleet/server/integration_tests/fleet_usage_telemetry.test.ts @@ -135,6 +135,7 @@ describe('fleet usage telemetry', () => { name: 'Ubuntu', version: '22.04.2 LTS (Jammy Jellyfish)', }, + elastic: { agent: { unprivileged: false } }, // Root agent }, components: [ { @@ -172,6 +173,7 @@ describe('fleet usage telemetry', () => { name: 'Ubuntu', version: '20.04.5 LTS (Focal Fossa)', }, + elastic: { agent: { unprivileged: true } }, // Non root agent }, components: [ { @@ -482,6 +484,10 @@ describe('fleet usage telemetry', () => { num_host_urls: 0, }, packages: [], + agents_per_privileges: { + root: 3, + unprivileged: 1, + }, agents_per_version: [ { version: '8.6.0', diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index 22b5aa3f5fddb..f74366d924c96 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -85,6 +85,7 @@ import { migratePackagePolicyEvictionsFromV81102, } from './migrations/security_solution/to_v8_11_0_2'; import { settingsV1 } from './model_versions/v1'; +import { packagePolicyV10OnWriteScanFix } from './model_versions/security_solution'; /* * Saved object types and mappings @@ -540,6 +541,14 @@ export const getSavedObjectTypes = ( }, ], }, + '10': { + changes: [ + { + type: 'data_backfill', + backfillFn: packagePolicyV10OnWriteScanFix, + }, + ], + }, }, migrations: { '7.10.0': migratePackagePolicyToV7100, diff --git a/x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/index.ts b/x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/index.ts new file mode 100644 index 0000000000000..780bc55f9cd3e --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { packagePolicyV10OnWriteScanFix } from './v10_on_write_scan_fix'; diff --git a/x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/v10_on_write_scan_fix.test.ts b/x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/v10_on_write_scan_fix.test.ts new file mode 100644 index 0000000000000..37ca3dd6d4b0d --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/v10_on_write_scan_fix.test.ts @@ -0,0 +1,183 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { SavedObject } from '@kbn/core-saved-objects-api-server'; +import type { ModelVersionTestMigrator } from '@kbn/core-test-helpers-model-versions'; +import { createModelVersionTestMigrator } from '@kbn/core-test-helpers-model-versions'; + +import { getSavedObjectTypes } from '../..'; + +import type { PackagePolicy } from '../../../../common'; +import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../../../../common'; + +describe('backfill for modelVersion 10 - fix on_write_scan field', () => { + let migrator: ModelVersionTestMigrator; + let policyConfigSO: SavedObject; + + beforeEach(() => { + migrator = createModelVersionTestMigrator({ + type: getSavedObjectTypes()[PACKAGE_POLICY_SAVED_OBJECT_TYPE], + }); + + policyConfigSO = { + id: 'mock-saved-object-id', + attributes: { + name: 'Some Policy Name', + package: { + name: 'endpoint', + title: '', + version: '', + }, + id: 'endpoint', + policy_id: '', + enabled: true, + namespace: '', + revision: 0, + updated_at: '', + updated_by: '', + created_at: '', + created_by: '', + inputs: [ + { + type: 'endpoint', + enabled: true, + streams: [], + config: { + policy: { + value: { + windows: { + malware: { + mode: 'detect', + }, + antivirus_registration: { + enabled: true, + }, + }, + mac: { + malware: { + mode: 'detect', + }, + }, + linux: { + malware: { + mode: 'detect', + }, + }, + }, + }, + }, + }, + ], + }, + type: PACKAGE_POLICY_SAVED_OBJECT_TYPE, + references: [], + }; + }); + + describe('when updating to model version 10', () => { + it('should change `on_write_scan` from `true` to `false` if Malware is off', () => { + setMalwareMode(policyConfigSO, 'off'); + setOnWriteScan(policyConfigSO, true); + + const migratedPolicyConfigSO = migrator.migrate({ + document: policyConfigSO, + fromVersion: 9, + toVersion: 10, + }); + + expectOnWriteScanToBe(false, migratedPolicyConfigSO); + }); + + it('should not change `on_write_scan` if Malware is detect', () => { + setMalwareMode(policyConfigSO, 'detect'); + setOnWriteScan(policyConfigSO, true); + + const migratedPolicyConfigSO = migrator.migrate({ + document: policyConfigSO, + fromVersion: 9, + toVersion: 10, + }); + + expectOnWriteScanToBe(true, migratedPolicyConfigSO); + }); + + it('should not change `on_write_scan` if Malware is prevent', () => { + setMalwareMode(policyConfigSO, 'prevent'); + setOnWriteScan(policyConfigSO, true); + + const migratedPolicyConfigSO = migrator.migrate({ + document: policyConfigSO, + fromVersion: 9, + toVersion: 10, + }); + + expectOnWriteScanToBe(true, migratedPolicyConfigSO); + }); + }); + + describe('additional test: when updating from model version 5 to model version 10', () => { + it('should add `on_write_scan=false` if Malware is off', () => { + setMalwareMode(policyConfigSO, 'off'); + + const migratedPolicyConfigSO = migrator.migrate({ + document: policyConfigSO, + fromVersion: 5, + toVersion: 10, + }); + + expectOnWriteScanToBe(false, migratedPolicyConfigSO); + }); + + it('should add `on_write_scan=true` if Malware is detect', () => { + setMalwareMode(policyConfigSO, 'detect'); + + const migratedPolicyConfigSO = migrator.migrate({ + document: policyConfigSO, + fromVersion: 5, + toVersion: 10, + }); + + expectOnWriteScanToBe(true, migratedPolicyConfigSO); + }); + + it('should add `on_write_scan=true` if Malware is prevent', () => { + setMalwareMode(policyConfigSO, 'prevent'); + + const migratedPolicyConfigSO = migrator.migrate({ + document: policyConfigSO, + fromVersion: 5, + toVersion: 10, + }); + + expectOnWriteScanToBe(true, migratedPolicyConfigSO); + }); + }); + + const setMalwareMode = (so: SavedObject, level: 'off' | 'detect' | 'prevent') => { + const config = so.attributes.inputs[0].config?.policy.value; + + config.windows.malware.mode = level; + config.mac.malware.mode = level; + config.linux.malware.mode = level; + }; + + const setOnWriteScan = (so: SavedObject, value: boolean) => { + const config = so.attributes.inputs[0].config?.policy.value; + + config.windows.malware.on_write_scan = value; + config.mac.malware.on_write_scan = value; + config.linux.malware.on_write_scan = value; + }; + + const expectOnWriteScanToBe = (expectedValue: boolean, so: SavedObject) => { + const config = so.attributes.inputs[0].config?.policy.value; + + expect(config.windows.malware.on_write_scan).toBe(expectedValue); + expect(config.mac.malware.on_write_scan).toBe(expectedValue); + expect(config.linux.malware.on_write_scan).toBe(expectedValue); + }; +}); diff --git a/x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/v10_on_write_scan_fix.ts b/x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/v10_on_write_scan_fix.ts new file mode 100644 index 0000000000000..7f793f7980164 --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/v10_on_write_scan_fix.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { + SavedObjectModelDataBackfillFn, + SavedObjectUnsanitizedDoc, +} from '@kbn/core-saved-objects-server'; + +import type { PackagePolicy } from '../../../../common'; + +export const packagePolicyV10OnWriteScanFix: SavedObjectModelDataBackfillFn< + PackagePolicy, + PackagePolicy +> = (packagePolicyDoc) => { + if (packagePolicyDoc.attributes.package?.name !== 'endpoint') { + return { attributes: packagePolicyDoc.attributes }; + } + + const updatedPackagePolicyDoc: SavedObjectUnsanitizedDoc = packagePolicyDoc; + + const input = updatedPackagePolicyDoc.attributes.inputs[0]; + + if (input && input.config) { + const policy = input.config.policy.value; + + if (policy.windows.malware.mode === 'off') { + policy.windows.malware.on_write_scan = false; + } + if (policy.mac.malware.mode === 'off') { + policy.mac.malware.on_write_scan = false; + } + if (policy.linux.malware.mode === 'off') { + policy.linux.malware.on_write_scan = false; + } + } + + return { attributes: updatedPackagePolicyDoc.attributes }; +}; diff --git a/x-pack/plugins/fleet/server/services/metrics/fetch_agent_metrics.ts b/x-pack/plugins/fleet/server/services/metrics/fetch_agent_metrics.ts index 5662a1a8fa1c8..ac5e692b9023e 100644 --- a/x-pack/plugins/fleet/server/services/metrics/fetch_agent_metrics.ts +++ b/x-pack/plugins/fleet/server/services/metrics/fetch_agent_metrics.ts @@ -64,6 +64,7 @@ export const fetchAgentMetrics = async ( upgrading_step: await getUpgradingSteps(esClient, abortController), unhealthy_reason: await getUnhealthyReason(esClient, abortController), }; + return usage; }; diff --git a/x-pack/plugins/fleet/server/services/metrics/fleet_metrics_task.ts b/x-pack/plugins/fleet/server/services/metrics/fleet_metrics_task.ts index 6b58decb4d109..0d1c0bd142169 100644 --- a/x-pack/plugins/fleet/server/services/metrics/fleet_metrics_task.ts +++ b/x-pack/plugins/fleet/server/services/metrics/fleet_metrics_task.ts @@ -186,7 +186,7 @@ export class FleetMetricsTask { this.wasStarted = true; try { - appContextService.getLogger().info(`Task ${this.taskId} scheduled with interval 1h`); + appContextService.getLogger().info(`Task ${this.taskId} scheduled with interval ${INTERVAL}`); await this.taskManager.ensureScheduled({ id: this.taskId, diff --git a/x-pack/plugins/fleet/server/services/telemetry/fleet_usage_sender.ts b/x-pack/plugins/fleet/server/services/telemetry/fleet_usage_sender.ts index f0a79a2633036..367d7f0ce0ff9 100644 --- a/x-pack/plugins/fleet/server/services/telemetry/fleet_usage_sender.ts +++ b/x-pack/plugins/fleet/server/services/telemetry/fleet_usage_sender.ts @@ -83,6 +83,7 @@ export class FleetUsageSender { const { agents_per_version: agentsPerVersion, agents_per_output_type: agentsPerOutputType, + agents_per_privileges: agentsPerPrivileges, upgrade_details: upgradeDetails, ...fleetUsageData } = usageData; @@ -92,6 +93,13 @@ export class FleetUsageSender { core.analytics.reportEvent(FLEET_USAGES_EVENT_TYPE, fleetUsageData); + appContextService + .getLogger() + .debug('Agents per privileges telemetry: ' + JSON.stringify(agentsPerPrivileges)); + core.analytics.reportEvent(FLEET_AGENTS_EVENT_TYPE, { + agents_per_privileges: agentsPerPrivileges, + }); + appContextService .getLogger() .debug('Agents per version telemetry: ' + JSON.stringify(agentsPerVersion)); diff --git a/x-pack/plugins/fleet/server/services/telemetry/fleet_usages_schema.ts b/x-pack/plugins/fleet/server/services/telemetry/fleet_usages_schema.ts index 5deaa97b02f3f..574c5dac1ad6a 100644 --- a/x-pack/plugins/fleet/server/services/telemetry/fleet_usages_schema.ts +++ b/x-pack/plugins/fleet/server/services/telemetry/fleet_usages_schema.ts @@ -135,6 +135,26 @@ export const fleetAgentsSchema: RootSchema = { }, }, }, + agents_per_privileges: { + _meta: { + description: 'Agents per privileges telemetry', + optional: true, + }, + properties: { + root: { + type: 'long', + _meta: { + description: 'Number of agents running with root privilege', + }, + }, + unprivileged: { + type: 'long', + _meta: { + description: 'Number of agents running without root privilege', + }, + }, + }, + }, upgrade_details: { _meta: { description: 'Agent upgrade details telemetry', diff --git a/x-pack/plugins/lens/public/app_plugin/app.test.tsx b/x-pack/plugins/lens/public/app_plugin/app.test.tsx index 374d651c44d8f..8aebc4778e201 100644 --- a/x-pack/plugins/lens/public/app_plugin/app.test.tsx +++ b/x-pack/plugins/lens/public/app_plugin/app.test.tsx @@ -186,8 +186,8 @@ describe('Lens App', () => { query: { query: '', language: 'lucene' }, filters: [pinnedFilter], resolvedDateRange: { - fromDate: '2021-01-10T04:00:00.000Z', - toDate: '2021-01-10T08:00:00.000Z', + fromDate: 'now-7d', + toDate: 'now', }, }), }); @@ -1117,8 +1117,8 @@ describe('Lens App', () => { lens: expect.objectContaining({ query: { query: '', language: 'lucene' }, resolvedDateRange: { - fromDate: '2021-01-10T04:00:00.000Z', - toDate: '2021-01-10T08:00:00.000Z', + fromDate: 'now-7d', + toDate: 'now', }, }), }); @@ -1154,8 +1154,8 @@ describe('Lens App', () => { lens: expect.objectContaining({ query: { query: 'new', language: 'lucene' }, resolvedDateRange: { - fromDate: '2021-01-09T04:00:00.000Z', - toDate: '2021-01-09T08:00:00.000Z', + fromDate: 'now-14d', + toDate: 'now-7d', }, }), }); @@ -1454,8 +1454,8 @@ describe('Lens App', () => { type: 'lens/setState', payload: { resolvedDateRange: { - fromDate: '2021-01-10T04:00:00.000Z', - toDate: '2021-01-10T08:00:00.000Z', + fromDate: 'now-7d', + toDate: 'now', }, searchSessionId: 'sessionId-2', }, 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 index 0f9794ac77f31..2cc7791f9f941 100644 --- 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 @@ -37,7 +37,11 @@ import { useLensDispatch, } from '../../../state_management'; import type { TypedLensByValueInput } from '../../../embeddable/embeddable_component'; -import { EXPRESSION_BUILD_ERROR_ID, extractReferencesFromState } from '../../../utils'; +import { + EXPRESSION_BUILD_ERROR_ID, + extractReferencesFromState, + getAbsoluteDateRange, +} from '../../../utils'; import { LayerConfiguration } from './layer_configuration_section'; import type { EditConfigPanelProps } from './types'; import { FlyoutWrapper } from './flyout_wrapper'; @@ -94,6 +98,10 @@ export function LensEditConfigurationFlyout({ const framePublicAPI = useLensSelector((state) => selectFramePublicAPI(state, datasourceMap)); + framePublicAPI.absDateRange = getAbsoluteDateRange( + startDependencies.data.query.timefilter.timefilter + ); + const layers = useMemo( () => activeDatasource.getLayers(datasourceState), [activeDatasource, datasourceState] diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/editor/formula_editor.tsx b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/editor/formula_editor.tsx index d3f06e077d7f8..e182a3a84b979 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/editor/formula_editor.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/editor/formula_editor.tsx @@ -109,6 +109,7 @@ export function FormulaEditor({ hasData, dateRange, uiSettings, + data, }: Omit, 'activeData'> & { dateHistogramInterval: ReturnType; hasData: boolean; @@ -452,7 +453,7 @@ export function FormulaEditor({ unifiedSearch, dataViews, dateHistogramInterval: baseIntervalRef.current, - dateRange, + timefilter: data.query.timefilter.timefilter, }); } } else { @@ -465,7 +466,7 @@ export function FormulaEditor({ unifiedSearch, dataViews, dateHistogramInterval: baseIntervalRef.current, - dateRange, + timefilter: data.query.timefilter.timefilter, }); } @@ -481,7 +482,7 @@ export function FormulaEditor({ ), }; }, - [indexPattern, visibleOperationsMap, unifiedSearch, dataViews, baseIntervalRef, dateRange] + [indexPattern, visibleOperationsMap, unifiedSearch, dataViews, data.query.timefilter.timefilter] ); const provideSignatureHelp = useCallback( diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/editor/math_completion.test.ts b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/editor/math_completion.test.ts index 270aa5f2c1bb7..2dfe44f2086ff 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/editor/math_completion.test.ts +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/editor/math_completion.test.ts @@ -5,11 +5,13 @@ * 2.0. */ +import moment from 'moment'; import { parse } from '@kbn/tinymath'; import { monaco } from '@kbn/monaco'; import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; import { tinymathFunctions } from '@kbn/lens-formula-docs'; +import { TimefilterContract } from '@kbn/data-plugin/public'; import { createMockedIndexPattern } from '../../../../mocks'; import { GenericOperationDefinition } from '../..'; import type { OperationMetadata, IndexPatternField } from '../../../../../../types'; @@ -210,8 +212,6 @@ The total number of documents. When you provide a field, the total number of fie }); describe('autocomplete', () => { - const dateRange = { fromDate: '2022-11-01T00:00:00.000Z', toDate: '2022-11-03T00:00:00.000Z' }; - function getSuggestionArgs({ expression, zeroIndexedOffset, @@ -232,7 +232,13 @@ The total number of documents. When you provide a field, the total number of fie operationDefinitionMap, unifiedSearch: unifiedSearchPluginMock.createStartContract(), dataViews: dataViewPluginMocks.createStartContract(), - dateRange, + timefilter: { + getTime: () => ({ from: '2022-11-01T00:00:00.000Z', to: '2022-11-03T00:00:00.000Z' }), + calculateBounds: () => ({ + min: moment('2022-11-01T00:00:00.000Z'), + max: moment('2022-11-03T00:00:00.000Z'), + }), + } as unknown as TimefilterContract, }; } it('should list all valid functions at the top level (fake test)', async () => { diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/editor/math_completion.ts b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/editor/math_completion.ts index eac9f66c77107..a09bcaa245b17 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/editor/math_completion.ts +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/editor/math_completion.ts @@ -24,7 +24,8 @@ import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import { parseTimeShift } from '@kbn/data-plugin/common'; import { tinymathFunctions } from '@kbn/lens-formula-docs'; import moment from 'moment'; -import { nonNullable } from '../../../../../../utils'; +import { TimefilterContract } from '@kbn/data-plugin/public'; +import { getAbsoluteDateRange, nonNullable } from '../../../../../../utils'; import { DateRange } from '../../../../../../../common/types'; import type { IndexPattern } from '../../../../../../types'; import { memoizedGetAvailableOperationsByMetadata } from '../../../operations'; @@ -150,7 +151,7 @@ export async function suggest({ dataViews, unifiedSearch, dateHistogramInterval, - dateRange, + timefilter, }: { expression: string; zeroIndexedOffset: number; @@ -160,7 +161,7 @@ export async function suggest({ unifiedSearch: UnifiedSearchPublicPluginStart; dataViews: DataViewsPublicPluginStart; dateHistogramInterval?: number; - dateRange: DateRange; + timefilter: TimefilterContract; }): Promise { const text = expression.substr(0, zeroIndexedOffset) + MARKER + expression.substr(zeroIndexedOffset); @@ -170,6 +171,8 @@ export async function suggest({ const tokenInfo = getInfoAtZeroIndexedPosition(ast, zeroIndexedOffset); const tokenAst = tokenInfo?.ast; + const dateRange = getAbsoluteDateRange(timefilter); + const isNamedArgument = tokenInfo?.parent && typeof tokenAst !== 'number' && diff --git a/x-pack/plugins/lens/public/datasources/form_based/to_expression.ts b/x-pack/plugins/lens/public/datasources/form_based/to_expression.ts index feba325e5dfcf..743efc9cb8db7 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/to_expression.ts +++ b/x-pack/plugins/lens/public/datasources/form_based/to_expression.ts @@ -22,6 +22,7 @@ import { ExpressionAstExpressionBuilder, ExpressionAstFunction, } from '@kbn/expressions-plugin/public'; +import { convertToAbsoluteDateRange } from '../../utils'; import type { DateRange } from '../../../common/types'; import { GenericIndexPatternColumn } from './form_based'; import { operationDefinitionMap } from './operations'; @@ -162,6 +163,8 @@ function getExpressionForLayer( const orderedColumnIds = esAggEntries.map(([colId]) => colId); let esAggsIdMap: Record = {}; + + const absDateRange = convertToAbsoluteDateRange(dateRange, nowInstant); const aggExpressionToEsAggsIdMap: Map = new Map(); esAggEntries.forEach(([colId, col], index) => { const def = operationDefinitionMap[col.operationType]; @@ -179,7 +182,7 @@ function getExpressionForLayer( ...col, timeShift: resolveTimeShift( col.timeShift, - dateRange, + absDateRange, histogramBarsTarget, hasDateHistogram ), @@ -207,7 +210,7 @@ function getExpressionForLayer( timeWindow: wrapInTimeFilter ? col.reducedTimeRange : undefined, timeShift: resolveTimeShift( col.timeShift, - dateRange, + absDateRange, histogramBarsTarget, hasDateHistogram ), @@ -216,7 +219,7 @@ function getExpressionForLayer( customMetric: buildExpression({ type: 'expression', chain: [aggAst] }), timeShift: resolveTimeShift( col.timeShift, - dateRange, + absDateRange, histogramBarsTarget, hasDateHistogram ), 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 5835c33c8d54a..679de93b8ae1b 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 @@ -392,6 +392,7 @@ describe('ConfigPanel', () => { a: expect.anything(), }, dateRange: expect.anything(), + absDateRange: expect.anything(), filters: [], now: expect.anything(), query: undefined, diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx index 45b11b1c7724b..e0736b149fcbb 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx @@ -9,6 +9,7 @@ import React, { useCallback, useRef } from 'react'; import { CoreStart } from '@kbn/core/public'; import { ReactExpressionRendererType } from '@kbn/expressions-plugin/public'; import { type DragDropAction, DragDropIdentifier, RootDragDropProvider } from '@kbn/dom-drag-drop'; +import { getAbsoluteDateRange } from '../../utils'; import { trackUiCounterEvents } from '../../lens_ui_telemetry'; import { DatasourceMap, @@ -68,6 +69,10 @@ export function EditorFrame(props: EditorFrameProps) { selectFramePublicAPI(state, datasourceMap) ); + framePublicAPI.absDateRange = getAbsoluteDateRange( + props.plugins.data.query.timefilter.timefilter + ); + // Using a ref to prevent rerenders in the child components while keeping the latest state const getSuggestionForField = useRef<(field: DragDropIdentifier) => Suggestion | undefined>(); getSuggestionForField.current = (field: DragDropIdentifier) => { diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx index c659d726f7b8a..c574d575bd5d9 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx @@ -772,6 +772,7 @@ export const VisualizationWrapper = ({ className="lnsExpressionRenderer__component" padding={displayOptions?.noPadding ? undefined : 'm'} expression={expression!} + allowCache={true} searchContext={searchContext} searchSessionId={searchSessionId} onEvent={onEvent} diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index 3206947c2a9b7..daf1d078894e9 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -663,6 +663,10 @@ export class Embeddable fromDate: mergedSearchContext.timeRange?.from ?? '', toDate: mergedSearchContext.timeRange?.to ?? '', }, + absDateRange: { + fromDate: mergedSearchContext.timeRange?.from ?? '', + toDate: mergedSearchContext.timeRange?.to ?? '', + }, activeData: this.activeData, }; diff --git a/x-pack/plugins/lens/public/embeddable/expression_wrapper.tsx b/x-pack/plugins/lens/public/embeddable/expression_wrapper.tsx index a8477a549d3ca..d16df5bf9d1e8 100644 --- a/x-pack/plugins/lens/public/embeddable/expression_wrapper.tsx +++ b/x-pack/plugins/lens/public/embeddable/expression_wrapper.tsx @@ -80,6 +80,7 @@ export function ExpressionWrapper({ className="lnsExpressionRenderer__component" padding={noPadding ? undefined : 's'} variables={variables} + allowCache={true} expression={expression} interactive={interactive} searchContext={searchContext} diff --git a/x-pack/plugins/lens/public/mocks/index.ts b/x-pack/plugins/lens/public/mocks/index.ts index f8bf55e3c2e8f..91da76b4acee6 100644 --- a/x-pack/plugins/lens/public/mocks/index.ts +++ b/x-pack/plugins/lens/public/mocks/index.ts @@ -37,6 +37,10 @@ export type FrameMock = jest.Mocked; export const createMockFramePublicAPI = (overrides: Partial = {}): FrameMock => ({ datasourceLayers: {}, dateRange: { + fromDate: 'now-1d', + toDate: 'now', + }, + absDateRange: { fromDate: '2022-03-17T08:25:00.000Z', toDate: '2022-04-17T08:25:00.000Z', }, diff --git a/x-pack/plugins/lens/public/mocks/store_mocks.tsx b/x-pack/plugins/lens/public/mocks/store_mocks.tsx index 354b6cbac53c5..c1c3ada4ddf1c 100644 --- a/x-pack/plugins/lens/public/mocks/store_mocks.tsx +++ b/x-pack/plugins/lens/public/mocks/store_mocks.tsx @@ -46,7 +46,7 @@ export const defaultState = { searchSessionId: 'sessionId-1', filters: [], query: { language: 'lucene', query: '' }, - resolvedDateRange: { fromDate: '2021-01-10T04:00:00.000Z', toDate: '2021-01-10T08:00:00.000Z' }, + resolvedDateRange: { fromDate: 'now-7d', toDate: 'now' }, isFullscreenDatasource: false, isSaveable: false, isLoading: false, diff --git a/x-pack/plugins/lens/public/state_management/__snapshots__/load_initial.test.tsx.snap b/x-pack/plugins/lens/public/state_management/__snapshots__/load_initial.test.tsx.snap index 68d4e7d57e983..a1ae0da676803 100644 --- a/x-pack/plugins/lens/public/state_management/__snapshots__/load_initial.test.tsx.snap +++ b/x-pack/plugins/lens/public/state_management/__snapshots__/load_initial.test.tsx.snap @@ -90,8 +90,8 @@ Object { "query": "", }, "resolvedDateRange": Object { - "fromDate": "2021-01-10T04:00:00.000Z", - "toDate": "2021-01-10T08:00:00.000Z", + "fromDate": "now-7d", + "toDate": "now", }, "searchSessionId": "sessionId-1", "sharingSavedObjectProps": Object { diff --git a/x-pack/plugins/lens/public/state_management/context_middleware/index.test.ts b/x-pack/plugins/lens/public/state_management/context_middleware/index.test.ts index 24254f4469f54..65a80bb539bf5 100644 --- a/x-pack/plugins/lens/public/state_management/context_middleware/index.test.ts +++ b/x-pack/plugins/lens/public/state_management/context_middleware/index.test.ts @@ -69,8 +69,8 @@ describe('contextMiddleware', () => { expect(store.dispatch).toHaveBeenCalledWith({ payload: { resolvedDateRange: { - fromDate: '2021-01-10T04:00:00.000Z', - toDate: '2021-01-10T08:00:00.000Z', + fromDate: 'now-2m', + toDate: 'now', }, searchSessionId: 'sessionId-1', }, diff --git a/x-pack/plugins/lens/public/state_management/selectors.ts b/x-pack/plugins/lens/public/state_management/selectors.ts index 58c7d981dcb41..2187302ae02e4 100644 --- a/x-pack/plugins/lens/public/state_management/selectors.ts +++ b/x-pack/plugins/lens/public/state_management/selectors.ts @@ -237,6 +237,7 @@ export const selectFramePublicAPI = createSelector( activeData, dataViews, ...context, + absDateRange: context.dateRange, }; } ); diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 9175dce78ec17..9fab715a08d82 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -955,6 +955,7 @@ export interface FramePublicAPI { filters: Filter[]; datasourceLayers: DatasourceLayers; dateRange: DateRange; + absDateRange: DateRange; /** * Data of the chart currently rendered in the preview. * This data might be not available (e.g. if the chart can't be rendered) or outdated and belonging to another chart. diff --git a/x-pack/plugins/lens/public/utils.ts b/x-pack/plugins/lens/public/utils.ts index d16dacada6fe1..83d0b841d0b2a 100644 --- a/x-pack/plugins/lens/public/utils.ts +++ b/x-pack/plugins/lens/public/utils.ts @@ -18,6 +18,8 @@ import { emptyTitleText } from '@kbn/visualization-ui-components'; import { RequestAdapter } from '@kbn/inspector-plugin/common'; import { ISearchStart } from '@kbn/data-plugin/public'; import type { DraggingIdentifier, DropType } from '@kbn/dom-drag-drop'; +import { getAbsoluteTimeRange } from '@kbn/data-plugin/common'; +import { DateRange } from '../common/types'; import type { Document } from './persistence/saved_object_store'; import { Datasource, @@ -46,6 +48,11 @@ export function getVisualizeGeoFieldMessage(fieldType: string) { } export const getResolvedDateRange = function (timefilter: TimefilterContract) { + const { from, to } = timefilter.getTime(); + return { fromDate: from, toDate: to }; +}; + +export const getAbsoluteDateRange = function (timefilter: TimefilterContract) { const { from, to } = timefilter.getTime(); const { min, max } = timefilter.calculateBounds({ from, @@ -54,6 +61,21 @@ export const getResolvedDateRange = function (timefilter: TimefilterContract) { return { fromDate: min?.toISOString() || from, toDate: max?.toISOString() || to }; }; +export const convertToAbsoluteDateRange = function (dateRange: DateRange, now: Date) { + const absRange = getAbsoluteTimeRange( + { + from: dateRange.fromDate as string, + to: dateRange.toDate as string, + }, + { forceNow: now } + ); + + return { + fromDate: absRange.from, + toDate: absRange.to, + }; +}; + export function containsDynamicMath(dateMathString: string) { return dateMathString.includes('now'); } diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.test.ts b/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.test.ts index d953814621aad..f6a601c06a163 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.test.ts @@ -16,7 +16,10 @@ describe('annotations helpers', () => { getStaticDate( [], createMockFramePublicAPI({ - dateRange: { fromDate: '2022-02-01T00:00:00.000Z', toDate: '2022-04-20T00:00:00.000Z' }, + absDateRange: { + fromDate: '2022-02-01T00:00:00.000Z', + toDate: '2022-04-20T00:00:00.000Z', + }, }) ) ).toBe('2022-03-12T00:00:00.000Z'); @@ -34,7 +37,10 @@ describe('annotations helpers', () => { }, ], createMockFramePublicAPI({ - dateRange: { fromDate: '2022-02-01T00:00:00.000Z', toDate: '2022-04-20T00:00:00.000Z' }, + absDateRange: { + fromDate: '2022-02-01T00:00:00.000Z', + toDate: '2022-04-20T00:00:00.000Z', + }, }) ) ).toBe('2022-03-12T00:00:00.000Z'); @@ -76,7 +82,10 @@ describe('annotations helpers', () => { }, ], createMockFramePublicAPI({ - dateRange: { fromDate: '2022-02-01T00:00:00.000Z', toDate: '2022-04-20T00:00:00.000Z' }, + absDateRange: { + fromDate: '2022-02-01T00:00:00.000Z', + toDate: '2022-04-20T00:00:00.000Z', + }, activeData, }) ) @@ -119,7 +128,10 @@ describe('annotations helpers', () => { }, ], createMockFramePublicAPI({ - dateRange: { fromDate: '2022-02-01T00:00:00.000Z', toDate: '2022-04-20T00:00:00.000Z' }, + absDateRange: { + fromDate: '2022-02-01T00:00:00.000Z', + toDate: '2022-04-20T00:00:00.000Z', + }, activeData, }) ) @@ -174,7 +186,10 @@ describe('annotations helpers', () => { }, ], createMockFramePublicAPI({ - dateRange: { fromDate: '2022-02-01T00:00:00.000Z', toDate: '2022-04-20T00:00:00.000Z' }, + absDateRange: { + fromDate: '2022-02-01T00:00:00.000Z', + toDate: '2022-04-20T00:00:00.000Z', + }, activeData, }) ) @@ -263,7 +278,10 @@ describe('annotations helpers', () => { createMockFramePublicAPI({ activeData, - dateRange: { fromDate: '2020-02-01T00:00:00.000Z', toDate: '2022-09-20T00:00:00.000Z' }, + absDateRange: { + fromDate: '2020-02-01T00:00:00.000Z', + toDate: '2022-09-20T00:00:00.000Z', + }, }) ) ).toBe('2020-08-24T12:06:40.000Z'); diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx index 0c821775dff20..7914bf81a717a 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx @@ -45,10 +45,10 @@ export const defaultRangeAnnotationLabel = i18n.translate( export function getStaticDate(dataLayers: XYDataLayerConfig[], frame: FramePublicAPI) { const dataLayersId = dataLayers.map(({ layerId }) => layerId); - const { activeData, dateRange } = frame; + const { activeData, absDateRange } = frame; - const dateRangeMinValue = moment(dateRange.fromDate).valueOf(); - const dateRangeMaxValue = moment(dateRange.toDate).valueOf(); + const dateRangeMinValue = moment(absDateRange.fromDate).valueOf(); + const dateRangeMaxValue = moment(absDateRange.toDate).valueOf(); const fallbackValue = moment((dateRangeMinValue + dateRangeMaxValue) / 2).toISOString(); if ( diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.tsx index 4967dd7877dad..750b493ea4b69 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.tsx @@ -846,6 +846,7 @@ describe('xy_visualization', () => { }, }, dateRange: { fromDate: '2022-04-10T00:00:00.000Z', toDate: '2022-04-20T00:00:00.000Z' }, + absDateRange: { fromDate: '2022-04-10T00:00:00.000Z', toDate: '2022-04-20T00:00:00.000Z' }, }; }); diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/annotations_panel.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/annotations_panel.tsx index db4d43d01b963..a53b38dc38e33 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/annotations_panel.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/annotations_panel.tsx @@ -111,12 +111,12 @@ export const AnnotationsPanel = ( const getEndTimestamp = ( datatableUtilities: DatatableUtilitiesService, startTime: string, - { activeData, dateRange }: FramePublicAPI, + { activeData, absDateRange }: FramePublicAPI, dataLayers: XYDataLayerConfig[] ) => { const startTimeNumber = moment(startTime).valueOf(); const dateRangeFraction = - (moment(dateRange.toDate).valueOf() - moment(dateRange.fromDate).valueOf()) * 0.1; + (moment(absDateRange.toDate).valueOf() - moment(absDateRange.fromDate).valueOf()) * 0.1; const fallbackValue = moment(startTimeNumber + dateRangeFraction).toISOString(); const dataLayersId = dataLayers.map(({ layerId }) => layerId); if ( diff --git a/x-pack/plugins/ml/public/application/components/help_popover/help_popover.tsx b/x-pack/plugins/ml/public/application/components/help_popover/help_popover.tsx index aa7bf496b1051..eccfe901d3c3b 100644 --- a/x-pack/plugins/ml/public/application/components/help_popover/help_popover.tsx +++ b/x-pack/plugins/ml/public/application/components/help_popover/help_popover.tsx @@ -51,7 +51,7 @@ export const HelpPopover: FC> = ({ > {title && {title}} - + {children} diff --git a/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.js b/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.js index 1f92062715183..795ed8ee16ffc 100644 --- a/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.js +++ b/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.js @@ -337,12 +337,13 @@ class RuleEditorFlyoutUI extends Component { updateRuleAtIndex = (ruleIndex, editedRule) => { const { toasts } = this.props.kibana.services.notifications; + const { mlApiServices, mlJobService } = this.props.kibana.services.mlServices; const { job, anomaly } = this.state; const jobId = job.job_id; const detectorIndex = anomaly.detectorIndex; - saveJobRule(job, detectorIndex, ruleIndex, editedRule) + saveJobRule(job, detectorIndex, ruleIndex, editedRule, mlApiServices, mlJobService) .then((resp) => { if (resp.success) { toasts.add({ @@ -391,11 +392,12 @@ class RuleEditorFlyoutUI extends Component { deleteRuleAtIndex = (index) => { const { toasts } = this.props.kibana.services.notifications; + const { mlApiServices, mlJobService: jobService } = this.props.kibana.services.mlServices; const { job, anomaly } = this.state; const jobId = job.job_id; const detectorIndex = anomaly.detectorIndex; - deleteJobRule(job, detectorIndex, index) + deleteJobRule(job, detectorIndex, index, mlApiServices, jobService) .then((resp) => { if (resp.success) { toasts.addSuccess( @@ -443,7 +445,8 @@ class RuleEditorFlyoutUI extends Component { addItemToFilterList = (item, filterId, closeFlyoutOnAdd) => { const { toasts } = this.props.kibana.services.notifications; - addItemToFilter(item, filterId) + const { mlApiServices } = this.props.kibana.services.mlServices; + addItemToFilter(item, filterId, mlApiServices) .then(() => { if (closeFlyoutOnAdd === true) { toasts.add({ diff --git a/x-pack/plugins/ml/public/application/components/rule_editor/utils.js b/x-pack/plugins/ml/public/application/components/rule_editor/utils.js index fd51ccd7b6b3c..8d0e89ed6b58f 100644 --- a/x-pack/plugins/ml/public/application/components/rule_editor/utils.js +++ b/x-pack/plugins/ml/public/application/components/rule_editor/utils.js @@ -15,8 +15,7 @@ import { ML_DETECTOR_RULE_OPERATOR, } from '@kbn/ml-anomaly-utils'; -import { ml } from '../../services/ml_api_service'; -import { mlJobService } from '../../services/job_service'; +import { mlJobService as importedMlJobService } from '../../services/job_service'; import { processCreatedBy } from '../../../../common/util/job_utils'; export function getNewConditionDefaults() { @@ -70,8 +69,16 @@ export function isValidRule(rule) { return isValid; } -export function saveJobRule(job, detectorIndex, ruleIndex, editedRule) { +export function saveJobRule( + job, + detectorIndex, + ruleIndex, + editedRule, + mlApiServices, + mlJobService +) { const detector = job.analysis_config.detectors[detectorIndex]; + const jobService = mlJobService || importedMlJobService; // Filter out any scope expression where the UI=specific 'enabled' // property is set to false. @@ -103,16 +110,17 @@ export function saveJobRule(job, detectorIndex, ruleIndex, editedRule) { } } - return updateJobRules(job, detectorIndex, rules); + return updateJobRules(job, detectorIndex, rules, mlApiServices, jobService); } -export function deleteJobRule(job, detectorIndex, ruleIndex) { +export function deleteJobRule(job, detectorIndex, ruleIndex, mlApiServices, mlJobService) { + const jobService = mlJobService || importedMlJobService; const detector = job.analysis_config.detectors[detectorIndex]; let customRules = []; if (detector.custom_rules !== undefined && ruleIndex < detector.custom_rules.length) { customRules = cloneDeep(detector.custom_rules); customRules.splice(ruleIndex, 1); - return updateJobRules(job, detectorIndex, customRules); + return updateJobRules(job, detectorIndex, customRules, mlApiServices, jobService); } else { return Promise.reject( new Error( @@ -128,7 +136,7 @@ export function deleteJobRule(job, detectorIndex, ruleIndex) { } } -export function updateJobRules(job, detectorIndex, rules) { +export function updateJobRules(job, detectorIndex, rules, mlApiServices, mlJobService) { // Pass just the detector with the edited rule to the updateJob endpoint. const jobId = job.job_id; const jobData = { @@ -146,9 +154,9 @@ export function updateJobRules(job, detectorIndex, rules) { processCreatedBy(customSettings); jobData.custom_settings = customSettings; } - return new Promise((resolve, reject) => { - ml.updateJob({ jobId: jobId, job: jobData }) + mlApiServices + .updateJob({ jobId: jobId, job: jobData }) .then(() => { // Refresh the job data in the job service before resolving. mlJobService @@ -168,9 +176,9 @@ export function updateJobRules(job, detectorIndex, rules) { // Updates an ML filter used in the scope part of a rule, // adding an item to the filter with the specified ID. -export function addItemToFilter(item, filterId) { +export function addItemToFilter(item, filterId, mlApiServices) { return new Promise((resolve, reject) => { - ml.filters + mlApiServices.filters .updateFilter(filterId, undefined, [item], undefined) .then((updatedFilter) => { resolve(updatedFilter); diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/waterfall_item.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/waterfall_item.tsx index ed21a170c6a90..6260c37ffdc5a 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/waterfall_item.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/waterfall_item.tsx @@ -125,8 +125,10 @@ interface IWaterfallItemProps { function PrefixIcon({ item }: { item: IWaterfallSpanOrTransaction }) { switch (item.docType) { case 'span': { + const spanType = item.doc.span.type || ''; + // icon for database spans - const isDbType = item.doc.span.type.startsWith('db'); + const isDbType = spanType.startsWith('db'); if (isDbType) { return ; } diff --git a/x-pack/plugins/observability_solution/apm/server/assistant_functions/get_apm_dataset_info.ts b/x-pack/plugins/observability_solution/apm/server/assistant_functions/get_apm_dataset_info.ts index e0f3f82128ddd..72fb4c8c7d200 100644 --- a/x-pack/plugins/observability_solution/apm/server/assistant_functions/get_apm_dataset_info.ts +++ b/x-pack/plugins/observability_solution/apm/server/assistant_functions/get_apm_dataset_info.ts @@ -24,7 +24,6 @@ export function registerGetApmDatasetInfoFunction({ description: `Use this function to get information about APM data.`, parameters: { type: 'object', - additionalProperties: false, properties: { start: { type: 'string', @@ -96,6 +95,15 @@ export function registerGetApmDatasetInfoFunction({ ); }); + if (!Object.values(availableIndices).flat().length) { + return { + content: { + fields: [], + description: 'There is no APM data available', + }, + }; + } + return { content: { fields: [ diff --git a/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts b/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts index 570328da84ea0..7bf03245d039e 100644 --- a/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts +++ b/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts @@ -230,10 +230,9 @@ export const tasks: TelemetryTask[] = [ { terms: { script: ` - if (doc['transaction.type'].value == 'page-load' && doc['user_agent.name'].size() > 0) { - return doc['user_agent.name'].value; + if ($('transaction.type', '') == 'page-load') { + return $('user_agent.name', null); } - return null; `, missing_bucket: true, @@ -242,7 +241,7 @@ export const tasks: TelemetryTask[] = [ // transaction.root { terms: { - script: `return doc['parent.id'].size() == 0`, + script: `return $('parent.id', '') == ''`, missing_bucket: true, }, }, @@ -259,8 +258,8 @@ export const tasks: TelemetryTask[] = [ { terms: { script: ` - if (doc['transaction.type'].value == 'page-load' && doc['client.geo.country_iso_code'].size() > 0) { - return doc['client.geo.country_iso_code'].value; + if ($('transaction.type', '') == 'page-load') { + return $('client.geo.country_iso_code', null); } return null; `, diff --git a/x-pack/plugins/observability_solution/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.test.ts b/x-pack/plugins/observability_solution/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.test.ts index 43b31c588f422..9cab31e30af64 100644 --- a/x-pack/plugins/observability_solution/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.test.ts +++ b/x-pack/plugins/observability_solution/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.test.ts @@ -74,7 +74,7 @@ describe('APMEventClient', () => { resolve(undefined); }, 100); }); - incomingRequest.abort(); + void incomingRequest.abort(); }, 100); }); diff --git a/x-pack/plugins/observability_solution/apm/server/routes/metrics/__snapshots__/queries.test.ts.snap b/x-pack/plugins/observability_solution/apm/server/routes/metrics/__snapshots__/queries.test.ts.snap index 44e4c6335ec7c..3fac4d595a274 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/metrics/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/observability_solution/apm/server/routes/metrics/__snapshots__/queries.test.ts.snap @@ -206,15 +206,14 @@ Object { */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; - String limitKey = 'system.process.cgroup.memory.mem.limit.bytes'; - - //Should use cgropLimit when value is not empty and not equals to the max limit value. - boolean useCgroupLimit = doc.containsKey(limitKey) && !doc[limitKey].empty && doc[limitKey].value != CGROUP_LIMIT_MAX_VALUE; - - double total = useCgroupLimit ? doc[limitKey].value : doc['system.memory.total'].value; - - double used = doc['system.process.cgroup.memory.mem.usage.bytes'].value; - + //Should use cgroupLimit when value is not empty and not equals to the max limit value. + double cgroupLimit = $('system.process.cgroup.memory.mem.limit.bytes', 0); + double total = (double)((cgroupLimit != 0 && cgroupLimit != CGROUP_LIMIT_MAX_VALUE) ? cgroupLimit : $('system.memory.total', 0)); + if (total <= 0) { + return null; + } + + double used = (double)$('system.process.cgroup.memory.mem.usage.bytes', 0); return used / total; ", }, @@ -231,15 +230,14 @@ Object { */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; - String limitKey = 'system.process.cgroup.memory.mem.limit.bytes'; - - //Should use cgropLimit when value is not empty and not equals to the max limit value. - boolean useCgroupLimit = doc.containsKey(limitKey) && !doc[limitKey].empty && doc[limitKey].value != CGROUP_LIMIT_MAX_VALUE; - - double total = useCgroupLimit ? doc[limitKey].value : doc['system.memory.total'].value; - - double used = doc['system.process.cgroup.memory.mem.usage.bytes'].value; + //Should use cgroupLimit when value is not empty and not equals to the max limit value. + double cgroupLimit = $('system.process.cgroup.memory.mem.limit.bytes', 0); + double total = (double)((cgroupLimit != 0 && cgroupLimit != CGROUP_LIMIT_MAX_VALUE) ? cgroupLimit : $('system.memory.total', 0)); + if (total <= 0) { + return null; + } + double used = (double)$('system.process.cgroup.memory.mem.usage.bytes', 0); return used / total; ", }, @@ -258,15 +256,14 @@ Object { */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; - String limitKey = 'system.process.cgroup.memory.mem.limit.bytes'; - - //Should use cgropLimit when value is not empty and not equals to the max limit value. - boolean useCgroupLimit = doc.containsKey(limitKey) && !doc[limitKey].empty && doc[limitKey].value != CGROUP_LIMIT_MAX_VALUE; - - double total = useCgroupLimit ? doc[limitKey].value : doc['system.memory.total'].value; - - double used = doc['system.process.cgroup.memory.mem.usage.bytes'].value; + //Should use cgroupLimit when value is not empty and not equals to the max limit value. + double cgroupLimit = $('system.process.cgroup.memory.mem.limit.bytes', 0); + double total = (double)((cgroupLimit != 0 && cgroupLimit != CGROUP_LIMIT_MAX_VALUE) ? cgroupLimit : $('system.memory.total', 0)); + if (total <= 0) { + return null; + } + double used = (double)$('system.process.cgroup.memory.mem.usage.bytes', 0); return used / total; ", }, @@ -283,15 +280,14 @@ Object { */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; - String limitKey = 'system.process.cgroup.memory.mem.limit.bytes'; - - //Should use cgropLimit when value is not empty and not equals to the max limit value. - boolean useCgroupLimit = doc.containsKey(limitKey) && !doc[limitKey].empty && doc[limitKey].value != CGROUP_LIMIT_MAX_VALUE; - - double total = useCgroupLimit ? doc[limitKey].value : doc['system.memory.total'].value; - - double used = doc['system.process.cgroup.memory.mem.usage.bytes'].value; + //Should use cgroupLimit when value is not empty and not equals to the max limit value. + double cgroupLimit = $('system.process.cgroup.memory.mem.limit.bytes', 0); + double total = (double)((cgroupLimit != 0 && cgroupLimit != CGROUP_LIMIT_MAX_VALUE) ? cgroupLimit : $('system.memory.total', 0)); + if (total <= 0) { + return null; + } + double used = (double)$('system.process.cgroup.memory.mem.usage.bytes', 0); return used / total; ", }, @@ -756,15 +752,14 @@ Object { */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; - String limitKey = 'system.process.cgroup.memory.mem.limit.bytes'; - - //Should use cgropLimit when value is not empty and not equals to the max limit value. - boolean useCgroupLimit = doc.containsKey(limitKey) && !doc[limitKey].empty && doc[limitKey].value != CGROUP_LIMIT_MAX_VALUE; - - double total = useCgroupLimit ? doc[limitKey].value : doc['system.memory.total'].value; - - double used = doc['system.process.cgroup.memory.mem.usage.bytes'].value; + //Should use cgroupLimit when value is not empty and not equals to the max limit value. + double cgroupLimit = $('system.process.cgroup.memory.mem.limit.bytes', 0); + double total = (double)((cgroupLimit != 0 && cgroupLimit != CGROUP_LIMIT_MAX_VALUE) ? cgroupLimit : $('system.memory.total', 0)); + if (total <= 0) { + return null; + } + double used = (double)$('system.process.cgroup.memory.mem.usage.bytes', 0); return used / total; ", }, @@ -781,15 +776,14 @@ Object { */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; - String limitKey = 'system.process.cgroup.memory.mem.limit.bytes'; - - //Should use cgropLimit when value is not empty and not equals to the max limit value. - boolean useCgroupLimit = doc.containsKey(limitKey) && !doc[limitKey].empty && doc[limitKey].value != CGROUP_LIMIT_MAX_VALUE; - - double total = useCgroupLimit ? doc[limitKey].value : doc['system.memory.total'].value; - - double used = doc['system.process.cgroup.memory.mem.usage.bytes'].value; + //Should use cgroupLimit when value is not empty and not equals to the max limit value. + double cgroupLimit = $('system.process.cgroup.memory.mem.limit.bytes', 0); + double total = (double)((cgroupLimit != 0 && cgroupLimit != CGROUP_LIMIT_MAX_VALUE) ? cgroupLimit : $('system.memory.total', 0)); + if (total <= 0) { + return null; + } + double used = (double)$('system.process.cgroup.memory.mem.usage.bytes', 0); return used / total; ", }, @@ -808,15 +802,14 @@ Object { */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; - String limitKey = 'system.process.cgroup.memory.mem.limit.bytes'; - - //Should use cgropLimit when value is not empty and not equals to the max limit value. - boolean useCgroupLimit = doc.containsKey(limitKey) && !doc[limitKey].empty && doc[limitKey].value != CGROUP_LIMIT_MAX_VALUE; - - double total = useCgroupLimit ? doc[limitKey].value : doc['system.memory.total'].value; - - double used = doc['system.process.cgroup.memory.mem.usage.bytes'].value; + //Should use cgroupLimit when value is not empty and not equals to the max limit value. + double cgroupLimit = $('system.process.cgroup.memory.mem.limit.bytes', 0); + double total = (double)((cgroupLimit != 0 && cgroupLimit != CGROUP_LIMIT_MAX_VALUE) ? cgroupLimit : $('system.memory.total', 0)); + if (total <= 0) { + return null; + } + double used = (double)$('system.process.cgroup.memory.mem.usage.bytes', 0); return used / total; ", }, @@ -833,15 +826,14 @@ Object { */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; - String limitKey = 'system.process.cgroup.memory.mem.limit.bytes'; - - //Should use cgropLimit when value is not empty and not equals to the max limit value. - boolean useCgroupLimit = doc.containsKey(limitKey) && !doc[limitKey].empty && doc[limitKey].value != CGROUP_LIMIT_MAX_VALUE; - - double total = useCgroupLimit ? doc[limitKey].value : doc['system.memory.total'].value; - - double used = doc['system.process.cgroup.memory.mem.usage.bytes'].value; + //Should use cgroupLimit when value is not empty and not equals to the max limit value. + double cgroupLimit = $('system.process.cgroup.memory.mem.limit.bytes', 0); + double total = (double)((cgroupLimit != 0 && cgroupLimit != CGROUP_LIMIT_MAX_VALUE) ? cgroupLimit : $('system.memory.total', 0)); + if (total <= 0) { + return null; + } + double used = (double)$('system.process.cgroup.memory.mem.usage.bytes', 0); return used / total; ", }, @@ -1302,15 +1294,14 @@ Object { */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; - String limitKey = 'system.process.cgroup.memory.mem.limit.bytes'; - - //Should use cgropLimit when value is not empty and not equals to the max limit value. - boolean useCgroupLimit = doc.containsKey(limitKey) && !doc[limitKey].empty && doc[limitKey].value != CGROUP_LIMIT_MAX_VALUE; - - double total = useCgroupLimit ? doc[limitKey].value : doc['system.memory.total'].value; - - double used = doc['system.process.cgroup.memory.mem.usage.bytes'].value; + //Should use cgroupLimit when value is not empty and not equals to the max limit value. + double cgroupLimit = $('system.process.cgroup.memory.mem.limit.bytes', 0); + double total = (double)((cgroupLimit != 0 && cgroupLimit != CGROUP_LIMIT_MAX_VALUE) ? cgroupLimit : $('system.memory.total', 0)); + if (total <= 0) { + return null; + } + double used = (double)$('system.process.cgroup.memory.mem.usage.bytes', 0); return used / total; ", }, @@ -1327,15 +1318,14 @@ Object { */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; - String limitKey = 'system.process.cgroup.memory.mem.limit.bytes'; - - //Should use cgropLimit when value is not empty and not equals to the max limit value. - boolean useCgroupLimit = doc.containsKey(limitKey) && !doc[limitKey].empty && doc[limitKey].value != CGROUP_LIMIT_MAX_VALUE; - - double total = useCgroupLimit ? doc[limitKey].value : doc['system.memory.total'].value; - - double used = doc['system.process.cgroup.memory.mem.usage.bytes'].value; + //Should use cgroupLimit when value is not empty and not equals to the max limit value. + double cgroupLimit = $('system.process.cgroup.memory.mem.limit.bytes', 0); + double total = (double)((cgroupLimit != 0 && cgroupLimit != CGROUP_LIMIT_MAX_VALUE) ? cgroupLimit : $('system.memory.total', 0)); + if (total <= 0) { + return null; + } + double used = (double)$('system.process.cgroup.memory.mem.usage.bytes', 0); return used / total; ", }, @@ -1354,15 +1344,14 @@ Object { */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; - String limitKey = 'system.process.cgroup.memory.mem.limit.bytes'; - - //Should use cgropLimit when value is not empty and not equals to the max limit value. - boolean useCgroupLimit = doc.containsKey(limitKey) && !doc[limitKey].empty && doc[limitKey].value != CGROUP_LIMIT_MAX_VALUE; - - double total = useCgroupLimit ? doc[limitKey].value : doc['system.memory.total'].value; - - double used = doc['system.process.cgroup.memory.mem.usage.bytes'].value; + //Should use cgroupLimit when value is not empty and not equals to the max limit value. + double cgroupLimit = $('system.process.cgroup.memory.mem.limit.bytes', 0); + double total = (double)((cgroupLimit != 0 && cgroupLimit != CGROUP_LIMIT_MAX_VALUE) ? cgroupLimit : $('system.memory.total', 0)); + if (total <= 0) { + return null; + } + double used = (double)$('system.process.cgroup.memory.mem.usage.bytes', 0); return used / total; ", }, @@ -1379,15 +1368,14 @@ Object { */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; - String limitKey = 'system.process.cgroup.memory.mem.limit.bytes'; - - //Should use cgropLimit when value is not empty and not equals to the max limit value. - boolean useCgroupLimit = doc.containsKey(limitKey) && !doc[limitKey].empty && doc[limitKey].value != CGROUP_LIMIT_MAX_VALUE; - - double total = useCgroupLimit ? doc[limitKey].value : doc['system.memory.total'].value; - - double used = doc['system.process.cgroup.memory.mem.usage.bytes'].value; + //Should use cgroupLimit when value is not empty and not equals to the max limit value. + double cgroupLimit = $('system.process.cgroup.memory.mem.limit.bytes', 0); + double total = (double)((cgroupLimit != 0 && cgroupLimit != CGROUP_LIMIT_MAX_VALUE) ? cgroupLimit : $('system.memory.total', 0)); + if (total <= 0) { + return null; + } + double used = (double)$('system.process.cgroup.memory.mem.usage.bytes', 0); return used / total; ", }, diff --git a/x-pack/plugins/observability_solution/apm/server/routes/metrics/by_agent/shared/memory/index.ts b/x-pack/plugins/observability_solution/apm/server/routes/metrics/by_agent/shared/memory/index.ts index 6fe3475abec84..af3e7301f8e61 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/metrics/by_agent/shared/memory/index.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/metrics/by_agent/shared/memory/index.ts @@ -56,12 +56,11 @@ export const systemMemory = { script: { lang: 'painless', source: ` - if(doc.containsKey('${METRIC_SYSTEM_FREE_MEMORY}') && doc.containsKey('${METRIC_SYSTEM_TOTAL_MEMORY}')){ - double freeMemoryValue = doc['${METRIC_SYSTEM_FREE_MEMORY}'].value; - double totalMemoryValue = doc['${METRIC_SYSTEM_TOTAL_MEMORY}'].value; - return 1 - freeMemoryValue / totalMemoryValue + def freeMemory = (double)$('${METRIC_SYSTEM_FREE_MEMORY}', 0); + def totalMemory = (double)$('${METRIC_SYSTEM_TOTAL_MEMORY}', -1); + if (freeMemory >= 0 && totalMemory > 0) { + return 1 - freeMemory / totalMemory; } - return null; `, }, @@ -87,15 +86,14 @@ export const cgroupMemory = { */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; - String limitKey = '${METRIC_CGROUP_MEMORY_LIMIT_BYTES}'; - - //Should use cgropLimit when value is not empty and not equals to the max limit value. - boolean useCgroupLimit = doc.containsKey(limitKey) && !doc[limitKey].empty && doc[limitKey].value != CGROUP_LIMIT_MAX_VALUE; - - double total = useCgroupLimit ? doc[limitKey].value : doc['${METRIC_SYSTEM_TOTAL_MEMORY}'].value; - - double used = doc['${METRIC_CGROUP_MEMORY_USAGE_BYTES}'].value; + //Should use cgroupLimit when value is not empty and not equals to the max limit value. + double cgroupLimit = $('${METRIC_CGROUP_MEMORY_LIMIT_BYTES}', 0); + double total = (double)((cgroupLimit != 0 && cgroupLimit != CGROUP_LIMIT_MAX_VALUE) ? cgroupLimit : $('${METRIC_SYSTEM_TOTAL_MEMORY}', 0)); + if (total <= 0) { + return null; + } + double used = (double)$('${METRIC_CGROUP_MEMORY_USAGE_BYTES}', 0); return used / total; `, }, diff --git a/x-pack/plugins/observability_solution/apm/server/routes/metrics/serverless/get_compute_usage_chart.ts b/x-pack/plugins/observability_solution/apm/server/routes/metrics/serverless/get_compute_usage_chart.ts index 60ff09131a868..3cdb1469295a9 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/metrics/serverless/get_compute_usage_chart.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/metrics/serverless/get_compute_usage_chart.ts @@ -25,7 +25,7 @@ import { convertComputeUsageToGbSec } from './helper'; export const computeUsageAvgScript = { avg: { - script: `return doc['${METRIC_SYSTEM_TOTAL_MEMORY}'].value * doc['${FAAS_BILLED_DURATION}'].value`, + script: `return $('${METRIC_SYSTEM_TOTAL_MEMORY}', 0) * $('${FAAS_BILLED_DURATION}', 0)`, }, }; diff --git a/x-pack/plugins/observability_solution/apm/server/routes/service_map/fetch_service_paths_from_trace_ids.ts b/x-pack/plugins/observability_solution/apm/server/routes/service_map/fetch_service_paths_from_trace_ids.ts index ad37caa865fb2..5467606954844 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/service_map/fetch_service_paths_from_trace_ids.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/service_map/fetch_service_paths_from_trace_ids.ts @@ -76,18 +76,18 @@ export async function fetchServicePathsFromTraceIds({ map_script: { lang: 'painless', source: `def id; - if (!doc['span.id'].empty) { - id = doc['span.id'].value; - } else { - id = doc['transaction.id'].value; + id = $('span.id', null); + if (id == null) { + id = $('transaction.id', null); } def copy = new HashMap(); copy.id = id; for(key in state.fieldsToCopy) { - if (!doc[key].empty) { - copy[key] = doc[key].value; + def value = $(key, null); + if (value != null) { + copy[key] = value; } } diff --git a/x-pack/plugins/observability_solution/apm/server/routes/traces/get_aggregated_critical_path.ts b/x-pack/plugins/observability_solution/apm/server/routes/traces/get_aggregated_critical_path.ts index f382085ad8dcc..6a3af27aa838d 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/traces/get_aggregated_critical_path.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/traces/get_aggregated_critical_path.ts @@ -123,38 +123,39 @@ export async function getAggregatedCriticalPath({ double duration; def operationMetadata = [ - "service.name": doc['service.name'].value, - "processor.event": doc['processor.event'].value, - "agent.name": doc['agent.name'].value + "service.name": $('service.name', ''), + "processor.event": $('processor.event', ''), + "agent.name": $('agent.name', '') ]; - def isSpan = !doc['span.id'].empty && !doc['span.name'].empty; - - if (isSpan) { - id = doc['span.id'].value; - operationMetadata.put('span.name', doc['span.name'].value); - if (!doc['span.type'].empty) { - operationMetadata.put('span.type', doc['span.type'].value); + def spanName = $('span.name', null); + id = $('span.id', null); + if (id != null && spanName != null) { + operationMetadata.put('span.name', spanName); + def spanType = $('span.type', ''); + if (spanType != '') { + operationMetadata.put('span.type', spanType); } - if (!doc['span.subtype'].empty) { - operationMetadata.put('span.subtype', doc['span.subtype'].value); + def spanSubtype = $('span.subtype', ''); + if (spanSubtype != '') { + operationMetadata.put('span.subtype', spanSubtype); } - duration = doc['span.duration.us'].value; + duration = $('span.duration.us', 0); } else { - id = doc['transaction.id'].value; - operationMetadata.put('transaction.name', doc['transaction.name'].value); - operationMetadata.put('transaction.type', doc['transaction.type'].value); - duration = doc['transaction.duration.us'].value; + id = $('transaction.id', ''); + operationMetadata.put('transaction.name', $('transaction.name', '')); + operationMetadata.put('transaction.type', $('transaction.type', '')); + duration = $('transaction.duration.us', 0); } String operationId = toHash(operationMetadata); def map = [ - "traceId": doc['trace.id'].value, + "traceId": $('trace.id', ''), "id": id, - "parentId": doc['parent.id'].empty ? null : doc['parent.id'].value, + "parentId": $('parent.id', null), "operationId": operationId, - "timestamp": doc['timestamp.us'].value, + "timestamp": $('timestamp.us', 0), "duration": duration ]; diff --git a/x-pack/plugins/observability_solution/apm/server/routes/traces/get_trace_items.ts b/x-pack/plugins/observability_solution/apm/server/routes/traces/get_trace_items.ts index da84cc208eddf..d38a49745653a 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/traces/get_trace_items.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/traces/get_trace_items.ts @@ -273,7 +273,7 @@ async function getTraceDocsPerPage({ type: 'number', script: { lang: 'painless', - source: `if (doc['${TRANSACTION_DURATION}'].size() > 0) { return doc['${TRANSACTION_DURATION}'].value } else { return doc['${SPAN_DURATION}'].value }`, + source: `$('${TRANSACTION_DURATION}', $('${SPAN_DURATION}', 0))`, }, order: 'desc', }, diff --git a/x-pack/plugins/observability_solution/asset_manager/common/constants_entities.ts b/x-pack/plugins/observability_solution/asset_manager/common/constants_entities.ts new file mode 100644 index 0000000000000..aeeea398220a0 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/common/constants_entities.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const ENTITY_VERSION = 'v1'; +export const ENTITY_BASE_PREFIX = `.entities-observability.summary-${ENTITY_VERSION}`; +export const ENTITY_TRANSFORM_PREFIX = `entities-observability-summary-${ENTITY_VERSION}`; +export const ENTITY_DEFAULT_FREQUENCY = '1m'; +export const ENTITY_DEFAULT_SYNC_DELAY = '60s'; +export const ENTITY_API_PREFIX = '/api/entities'; diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/create_and_install_ingest_pipeline.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/create_and_install_ingest_pipeline.ts new file mode 100644 index 0000000000000..e4d7116b95562 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/create_and_install_ingest_pipeline.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ElasticsearchClient, Logger } from '@kbn/core/server'; +import { EntityDefinition } from '@kbn/entities-schema'; +import { generateProcessors } from './ingest_pipeline/generate_processors'; +import { retryTransientEsErrors } from './helpers/retry'; +import { EntitySecurityException } from './errors/entity_security_exception'; +import { generateIngestPipelineId } from './ingest_pipeline/generate_ingest_pipeline_id'; + +export async function createAndInstallIngestPipeline( + esClient: ElasticsearchClient, + definition: EntityDefinition, + logger: Logger +) { + const processors = generateProcessors(definition); + const id = generateIngestPipelineId(definition); + try { + await retryTransientEsErrors( + () => + esClient.ingest.putPipeline({ + id, + processors, + }), + { logger } + ); + } catch (e) { + logger.error(`Cannot create entity ingest pipeline for [${definition.id}] entity defintion`); + if (e.meta?.body?.error?.type === 'security_exception') { + throw new EntitySecurityException(e.meta.body.error.reason, definition); + } + throw e; + } + return id; +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/create_and_install_transform.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/create_and_install_transform.ts new file mode 100644 index 0000000000000..f8cd02250d898 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/create_and_install_transform.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ElasticsearchClient, Logger } from '@kbn/core/server'; +import { EntityDefinition } from '@kbn/entities-schema'; +import { generateTransform } from './transform/generate_transform'; +import { retryTransientEsErrors } from './helpers/retry'; +import { EntitySecurityException } from './errors/entity_security_exception'; + +export async function createAndInstallTransform( + esClient: ElasticsearchClient, + definition: EntityDefinition, + logger: Logger +) { + const transform = generateTransform(definition); + try { + await retryTransientEsErrors(() => esClient.transform.putTransform(transform), { logger }); + } catch (e) { + logger.error(`Cannot create entity transform for [${definition.id}] entity definition`); + if (e.meta?.body?.error?.type === 'security_exception') { + throw new EntitySecurityException(e.meta.body.error.reason, definition); + } + throw e; + } + return transform.transform_id; +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/delete_entity_definition.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/delete_entity_definition.ts new file mode 100644 index 0000000000000..1067f33abaca3 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/delete_entity_definition.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Logger, SavedObjectsClientContract } from '@kbn/core/server'; +import { EntityDefinition } from '@kbn/entities-schema'; +import { SO_ENTITY_DEFINITION_TYPE } from '../../saved_objects'; +import { EntityDefinitionNotFound } from './errors/entity_not_found'; + +export async function deleteEntityDefinition( + soClient: SavedObjectsClientContract, + definition: EntityDefinition, + logger: Logger +) { + const response = await soClient.find({ + type: SO_ENTITY_DEFINITION_TYPE, + page: 1, + perPage: 1, + filter: `${SO_ENTITY_DEFINITION_TYPE}.attributes.id:(${definition.id})`, + }); + + if (response.total === 0) { + logger.error(`Unable to delete entity definition [${definition.id}] because it doesn't exist.`); + throw new EntityDefinitionNotFound(`Entity defintion with [${definition.id}] not found.`); + } + + await soClient.delete(SO_ENTITY_DEFINITION_TYPE, response.saved_objects[0].id); +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/delete_index.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/delete_index.ts new file mode 100644 index 0000000000000..7df6867115bbe --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/delete_index.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ElasticsearchClient, Logger } from '@kbn/core/server'; +import { EntityDefinition } from '@kbn/entities-schema'; +import { generateIndexName } from './helpers/generate_index_name'; + +export async function deleteIndex( + esClient: ElasticsearchClient, + definition: EntityDefinition, + logger: Logger +) { + const indexName = generateIndexName(definition); + try { + await esClient.indices.delete({ index: indexName, ignore_unavailable: true }); + } catch (e) { + logger.error(`Unable to remove entity defintion index [${definition.id}}]`); + throw e; + } +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/delete_ingest_pipeline.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/delete_ingest_pipeline.ts new file mode 100644 index 0000000000000..1e42282369ef3 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/delete_ingest_pipeline.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ElasticsearchClient, Logger } from '@kbn/core/server'; +import { EntityDefinition } from '@kbn/entities-schema'; +import { generateIngestPipelineId } from './ingest_pipeline/generate_ingest_pipeline_id'; +import { retryTransientEsErrors } from './helpers/retry'; + +export async function deleteIngestPipeline( + esClient: ElasticsearchClient, + definition: EntityDefinition, + logger: Logger +) { + const pipelineId = generateIngestPipelineId(definition); + try { + await retryTransientEsErrors(() => + esClient.ingest.deletePipeline({ id: pipelineId }, { ignore: [404] }) + ); + } catch (e) { + logger.error(`Unable to delete ingest pipeline [${pipelineId}]`); + throw e; + } +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/errors/entity_id_conflict_error.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/errors/entity_id_conflict_error.ts new file mode 100644 index 0000000000000..5108ca31ed548 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/errors/entity_id_conflict_error.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EntityDefinition } from '@kbn/entities-schema'; + +export class EntityIdConflict extends Error { + public defintion: EntityDefinition; + + constructor(message: string, def: EntityDefinition) { + super(message); + this.name = 'EntityIdConflict'; + this.defintion = def; + } +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/errors/entity_not_found.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/errors/entity_not_found.ts new file mode 100644 index 0000000000000..d81cd3322cae6 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/errors/entity_not_found.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export class EntityDefinitionNotFound extends Error { + constructor(message: string) { + super(message); + this.name = 'EntityDefinitionNotFound'; + } +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/errors/entity_security_exception.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/errors/entity_security_exception.ts new file mode 100644 index 0000000000000..6e2f721d4de14 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/errors/entity_security_exception.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EntityDefinition } from '@kbn/entities-schema'; + +export class EntitySecurityException extends Error { + public defintion: EntityDefinition; + + constructor(message: string, def: EntityDefinition) { + super(message); + this.name = 'EntitySecurityException'; + this.defintion = def; + } +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/errors/invalid_transform_error.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/errors/invalid_transform_error.ts new file mode 100644 index 0000000000000..5d1c98d5dc3ae --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/errors/invalid_transform_error.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export class InvalidTransformError extends Error { + constructor(message: string) { + super(message); + this.name = 'InvalidTransformError'; + } +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/helpers/fixtures/entity_definition.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/helpers/fixtures/entity_definition.ts new file mode 100644 index 0000000000000..fdb808466ba83 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/helpers/fixtures/entity_definition.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { entityDefinitionSchema } from '@kbn/entities-schema'; +export const entityDefinition = entityDefinitionSchema.parse({ + id: 'admin-console-logs-service', + name: 'Services for Admin Console', + type: 'service', + indexPatterns: ['kbn-data-forge-fake_stack.*'], + timestampField: '@timestamp', + identityFields: ['log.logger'], + identityTemplate: 'service:{{log.logger}}', + metadata: ['tags', 'host.name', 'kubernetes.pod.name'], + staticFields: { + projectId: '1234', + }, + lookback: '5m', + metrics: [ + { + name: 'logRate', + equation: 'A / 5', + metrics: [ + { + name: 'A', + aggregation: 'doc_count', + filter: 'log.level: *', + }, + ], + }, + { + name: 'errorRate', + equation: 'A / 5', + metrics: [ + { + name: 'A', + aggregation: 'doc_count', + filter: 'log.level: error', + }, + ], + }, + ], +}); diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/helpers/generate_index_name.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/helpers/generate_index_name.ts new file mode 100644 index 0000000000000..365104f3571eb --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/helpers/generate_index_name.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EntityDefinition } from '@kbn/entities-schema'; +import { ENTITY_BASE_PREFIX } from '../../../../common/constants_entities'; + +export function generateIndexName(definition: EntityDefinition) { + return `${ENTITY_BASE_PREFIX}.${definition.id}`; +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/helpers/get_elasticsearch_query_or_throw.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/helpers/get_elasticsearch_query_or_throw.ts new file mode 100644 index 0000000000000..3b24344efd83b --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/helpers/get_elasticsearch_query_or_throw.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; +import { InvalidTransformError } from '../errors/invalid_transform_error'; + +export function getElasticsearchQueryOrThrow(kuery: string) { + try { + return toElasticsearchQuery(fromKueryExpression(kuery)); + } catch (err) { + throw new InvalidTransformError(`Invalid KQL: ${kuery}`); + } +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/helpers/retry.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/helpers/retry.ts new file mode 100644 index 0000000000000..421289d1c0479 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/helpers/retry.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { setTimeout } from 'timers/promises'; +import { errors as EsErrors } from '@elastic/elasticsearch'; +import type { Logger } from '@kbn/logging'; + +const MAX_ATTEMPTS = 5; + +const retryResponseStatuses = [ + 503, // ServiceUnavailable + 408, // RequestTimeout + 410, // Gone +]; + +const isRetryableError = (e: any) => + e instanceof EsErrors.NoLivingConnectionsError || + e instanceof EsErrors.ConnectionError || + e instanceof EsErrors.TimeoutError || + (e instanceof EsErrors.ResponseError && retryResponseStatuses.includes(e?.statusCode!)); + +/** + * Retries any transient network or configuration issues encountered from Elasticsearch with an exponential backoff. + * Should only be used to wrap operations that are idempotent and can be safely executed more than once. + */ +export const retryTransientEsErrors = async ( + esCall: () => Promise, + { logger, attempt = 0 }: { logger?: Logger; attempt?: number } = {} +): Promise => { + try { + return await esCall(); + } catch (e) { + if (attempt < MAX_ATTEMPTS && isRetryableError(e)) { + const retryCount = attempt + 1; + const retryDelaySec = Math.min(Math.pow(2, retryCount), 64); // 2s, 4s, 8s, 16s, 32s, 64s, 64s, 64s ... + + logger?.warn( + `Retrying Elasticsearch operation after [${retryDelaySec}s] due to error: ${e.toString()} ${ + e.stack + }` + ); + + await setTimeout(retryDelaySec * 1000); + return retryTransientEsErrors(esCall, { logger, attempt: retryCount }); + } + + throw e; + } +}; diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/ingest_pipeline/__snapshots__/generate_processors.test.ts.snap b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/ingest_pipeline/__snapshots__/generate_processors.test.ts.snap new file mode 100644 index 0000000000000..443063d70db60 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/ingest_pipeline/__snapshots__/generate_processors.test.ts.snap @@ -0,0 +1,70 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`generateProcessors(definition) should genearte a valid pipeline 1`] = ` +Array [ + Object { + "set": Object { + "field": "event.ingested", + "value": "{{{_ingest.timestamp}}}", + }, + }, + Object { + "set": Object { + "field": "entity.definitionId", + "value": "admin-console-logs-service", + }, + }, + Object { + "set": Object { + "field": "entity.indexPatterns", + "value": "[\\"kbn-data-forge-fake_stack.*\\"]", + }, + }, + Object { + "json": Object { + "field": "entity.indexPatterns", + }, + }, + Object { + "set": Object { + "field": "entity.id", + "value": "service:{{entity.identity.log.logger}}", + }, + }, + Object { + "set": Object { + "field": "projectId", + "value": "1234", + }, + }, + Object { + "script": Object { + "source": "if (ctx.entity?.metadata?.tags != null) { + ctx[\\"tags\\"] = ctx.entity.metadata.tags.keySet(); +} +if (ctx.entity?.metadata?.host?.name != null) { + ctx[\\"host\\"] = new HashMap(); + ctx[\\"host\\"][\\"name\\"] = ctx.entity.metadata.host.name.keySet(); +} +if (ctx.entity?.metadata?.kubernetes?.pod?.name != null) { + ctx[\\"kubernetes\\"] = new HashMap(); + ctx[\\"kubernetes\\"][\\"pod\\"] = new HashMap(); + ctx[\\"kubernetes\\"][\\"pod\\"][\\"name\\"] = ctx.entity.metadata.kubernetes.pod.name.keySet(); +} +", + }, + }, + Object { + "remove": Object { + "field": "entity.metadata", + "ignore_missing": true, + }, + }, + Object { + "set": Object { + "field": "_index", + "value": ".entities-observability.summary-v1.admin-console-logs-service", + }, + }, +] +`; diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/ingest_pipeline/generate_ingest_pipeline_id.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/ingest_pipeline/generate_ingest_pipeline_id.ts new file mode 100644 index 0000000000000..c772e198e64fd --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/ingest_pipeline/generate_ingest_pipeline_id.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EntityDefinition } from '@kbn/entities-schema'; +import { ENTITY_BASE_PREFIX } from '../../../../common/constants_entities'; + +export function generateIngestPipelineId(definition: EntityDefinition) { + return `${ENTITY_BASE_PREFIX}.${definition.id}`; +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/ingest_pipeline/generate_processors.test.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/ingest_pipeline/generate_processors.test.ts new file mode 100644 index 0000000000000..33919ec678dcf --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/ingest_pipeline/generate_processors.test.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { generateProcessors } from './generate_processors'; +import { entityDefinition } from '../helpers/fixtures/entity_definition'; + +describe('generateProcessors(definition)', () => { + it('should genearte a valid pipeline', () => { + const processors = generateProcessors(entityDefinition); + expect(processors).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/ingest_pipeline/generate_processors.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/ingest_pipeline/generate_processors.ts new file mode 100644 index 0000000000000..33f27cc5daf71 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/ingest_pipeline/generate_processors.ts @@ -0,0 +1,98 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EntityDefinition } from '@kbn/entities-schema'; +import { generateIndexName } from '../helpers/generate_index_name'; + +function createIdTemplate(definition: EntityDefinition) { + return definition.identityFields.reduce((template, id) => { + return template.replaceAll(id.field, `entity.identity.${id.field}`); + }, definition.identityTemplate); +} + +function mapDesitnationToPainless(destination: string, source: string) { + const fieldParts = destination.split('.'); + return fieldParts.reduce((acc, _part, currentIndex, parts) => { + if (currentIndex + 1 === parts.length) { + return `${acc}\n ctx${parts + .map((s) => `["${s}"]`) + .join('')} = ctx.entity.metadata.${source}.keySet();`; + } + return `${acc}\n ctx${parts + .slice(0, currentIndex + 1) + .map((s) => `["${s}"]`) + .join('')} = new HashMap();`; + }, ''); +} + +function createMetadataPainlessScript(definition: EntityDefinition) { + if (!definition.metadata) { + return ''; + } + return definition.metadata.reduce((script, def) => { + const source = def.source; + const destination = def.destination || def.source; + return `${script}if (ctx.entity?.metadata?.${source.replaceAll( + '.', + '?.' + )} != null) {${mapDesitnationToPainless(destination, source)}\n}\n`; + }, ''); +} + +export function generateProcessors(definition: EntityDefinition) { + return [ + { + set: { + field: 'event.ingested', + value: '{{{_ingest.timestamp}}}', + }, + }, + { + set: { + field: 'entity.definitionId', + value: definition.id, + }, + }, + { + set: { + field: 'entity.indexPatterns', + value: JSON.stringify(definition.indexPatterns), + }, + }, + { + json: { + field: 'entity.indexPatterns', + }, + }, + { + set: { + field: 'entity.id', + value: createIdTemplate(definition), + }, + }, + ...(definition.staticFields != null + ? Object.keys(definition.staticFields).map((field) => ({ + set: { field, value: definition.staticFields![field] }, + })) + : []), + ...(definition.metadata != null + ? [{ script: { source: createMetadataPainlessScript(definition) } }] + : []), + { + remove: { + field: 'entity.metadata', + ignore_missing: true, + }, + }, + { + set: { + field: '_index', + value: generateIndexName(definition), + }, + }, + ]; +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/read_entity_definition.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/read_entity_definition.ts new file mode 100644 index 0000000000000..e6817ab63f2af --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/read_entity_definition.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Logger, SavedObjectsClientContract } from '@kbn/core/server'; +import { EntityDefinition, entityDefinitionSchema } from '@kbn/entities-schema'; +import { SO_ENTITY_DEFINITION_TYPE } from '../../saved_objects'; +import { EntityDefinitionNotFound } from './errors/entity_not_found'; + +export async function readEntityDefinition( + soClient: SavedObjectsClientContract, + id: string, + logger: Logger +) { + const response = await soClient.find({ + type: SO_ENTITY_DEFINITION_TYPE, + page: 1, + perPage: 1, + filter: `${SO_ENTITY_DEFINITION_TYPE}.attributes.id:(${id})`, + }); + if (response.total === 0) { + const message = `Unable to find entity defintion with [${id}]`; + logger.error(message); + throw new EntityDefinitionNotFound(message); + } + + try { + return entityDefinitionSchema.parse(response.saved_objects[0].attributes); + } catch (e) { + logger.error(`Unable to parse entity defintion with [${id}]`); + throw e; + } +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/save_entity_definition.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/save_entity_definition.ts new file mode 100644 index 0000000000000..f79f88c50ac58 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/save_entity_definition.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SavedObjectsClientContract } from '@kbn/core/server'; +import { EntityDefinition } from '@kbn/entities-schema'; +import { SO_ENTITY_DEFINITION_TYPE } from '../../saved_objects'; +import { EntityIdConflict } from './errors/entity_id_conflict_error'; + +export async function saveEntityDefinition( + soClient: SavedObjectsClientContract, + definition: EntityDefinition +): Promise { + const response = await soClient.find({ + type: SO_ENTITY_DEFINITION_TYPE, + page: 1, + perPage: 1, + filter: `${SO_ENTITY_DEFINITION_TYPE}.attributes.id:(${definition.id})`, + }); + + if (response.total === 1) { + throw new EntityIdConflict( + `Entity defintion with [${definition.id}] already exists.`, + definition + ); + } + + await soClient.create(SO_ENTITY_DEFINITION_TYPE, definition, { + id: definition.id, + overwrite: true, + }); + + return definition; +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/start_transform.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/start_transform.ts new file mode 100644 index 0000000000000..766bbb10b1d67 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/start_transform.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ElasticsearchClient, Logger } from '@kbn/core/server'; +import { EntityDefinition } from '@kbn/entities-schema'; +import { retryTransientEsErrors } from './helpers/retry'; +import { generateTransformId } from './transform/generate_transform_id'; + +export async function startTransform( + esClient: ElasticsearchClient, + definition: EntityDefinition, + logger: Logger +) { + const transformId = generateTransformId(definition); + try { + await retryTransientEsErrors( + () => esClient.transform.startTransform({ transform_id: transformId }, { ignore: [409] }), + { logger } + ); + } catch (err) { + logger.error(`Cannot start entity transform [${transformId}]`); + throw err; + } +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/stop_and_delete_transform.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/stop_and_delete_transform.ts new file mode 100644 index 0000000000000..60a250a33f0d9 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/stop_and_delete_transform.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ElasticsearchClient, Logger } from '@kbn/core/server'; +import { EntityDefinition } from '@kbn/entities-schema'; +import { generateTransformId } from './transform/generate_transform_id'; +import { retryTransientEsErrors } from './helpers/retry'; + +export async function stopAndDeleteTransform( + esClient: ElasticsearchClient, + definition: EntityDefinition, + logger: Logger +) { + const transformId = generateTransformId(definition); + try { + await retryTransientEsErrors( + async () => { + await esClient.transform.stopTransform( + { transform_id: transformId, wait_for_completion: true, force: true }, + { ignore: [409] } + ); + await esClient.transform.deleteTransform( + { transform_id: transformId, force: true }, + { ignore: [404] } + ); + }, + { logger } + ); + } catch (e) { + logger.error(`Cannot stop or delete entity transform [${transformId}]`); + throw e; + } +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/__snapshots__/generate_transform.test.ts.snap b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/__snapshots__/generate_transform.test.ts.snap new file mode 100644 index 0000000000000..e692f2068eafd --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/__snapshots__/generate_transform.test.ts.snap @@ -0,0 +1,126 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`generateTransform(definition) should generate a valid summary transform 1`] = ` +Object { + "defer_validation": true, + "dest": Object { + "index": ".entities-observability.summary-v1.noop", + "pipeline": ".entities-observability.summary-v1.admin-console-logs-service", + }, + "frequency": "1m", + "pivot": Object { + "aggs": Object { + "_errorRate_A": Object { + "filter": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match": Object { + "log.level": "error", + }, + }, + ], + }, + }, + }, + "_logRate_A": Object { + "filter": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "log.level", + }, + }, + ], + }, + }, + }, + "entity.latestTimestamp": Object { + "max": Object { + "field": "@timestamp", + }, + }, + "entity.metadata.host.name": Object { + "terms": Object { + "field": "host.name", + "size": 1000, + }, + }, + "entity.metadata.kubernetes.pod.name": Object { + "terms": Object { + "field": "kubernetes.pod.name", + "size": 1000, + }, + }, + "entity.metadata.tags": Object { + "terms": Object { + "field": "tags", + "size": 1000, + }, + }, + "entity.metric.errorRate": Object { + "bucket_script": Object { + "buckets_path": Object { + "A": "_errorRate_A>_count", + }, + "script": Object { + "lang": "painless", + "source": "params.A / 5", + }, + }, + }, + "entity.metric.logRate": Object { + "bucket_script": Object { + "buckets_path": Object { + "A": "_logRate_A>_count", + }, + "script": Object { + "lang": "painless", + "source": "params.A / 5", + }, + }, + }, + }, + "group_by": Object { + "entity.identity.log.logger": Object { + "terms": Object { + "field": "log.logger", + "missing_bucket": false, + }, + }, + }, + }, + "settings": Object { + "deduce_mappings": false, + "unattended": true, + }, + "source": Object { + "index": Array [ + "kbn-data-forge-fake_stack.*", + ], + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "range": Object { + "@timestamp": Object { + "gte": "now-5m", + }, + }, + }, + ], + }, + }, + }, + "sync": Object { + "time": Object { + "delay": "60s", + "field": "@timestamp", + }, + }, + "transform_id": "entities-observability-summary-v1-admin-console-logs-service", +} +`; diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_metadata_aggregations.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_metadata_aggregations.ts new file mode 100644 index 0000000000000..8c11aea138519 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_metadata_aggregations.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EntityDefinition } from '@kbn/entities-schema'; + +export function generateMetadataAggregations(definition: EntityDefinition) { + if (!definition.metadata) { + return {}; + } + return definition.metadata.reduce( + (aggs, metadata) => ({ + ...aggs, + [`entity.metadata.${metadata.destination ?? metadata.source}`]: { + terms: { + field: metadata.source, + size: metadata.limit ?? 1000, + }, + }, + }), + {} + ); +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_metric_aggregations.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_metric_aggregations.ts new file mode 100644 index 0000000000000..9527671768e35 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_metric_aggregations.ts @@ -0,0 +1,118 @@ +/* + * Copyright 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 { KeyMetric, Metric, EntityDefinition } from '@kbn/entities-schema'; +import { getElasticsearchQueryOrThrow } from '../helpers/get_elasticsearch_query_or_throw'; +import { InvalidTransformError } from '../errors/invalid_transform_error'; + +function buildAggregation(metric: Metric, timestampField: string) { + const { aggregation } = metric; + switch (aggregation) { + case 'doc_count': + return {}; + case 'std_deviation': + return { + extended_stats: { field: metric.field }, + }; + case 'percentile': + if (metric.percentile == null) { + throw new InvalidTransformError( + 'You must provide a percentile value for percentile aggregations.' + ); + } + return { + percentiles: { + field: metric.field, + percents: [metric.percentile], + keyed: true, + }, + }; + case 'last_value': + return { + top_metrics: { + metrics: { field: metric.field }, + sort: { [timestampField]: 'desc' }, + }, + }; + default: + if (metric.field == null) { + throw new InvalidTransformError('You must provide a field for basic metric aggregations.'); + } + return { + [aggregation]: { field: metric.field }, + }; + } +} + +function buildMetricAggregations(keyMetric: KeyMetric, timestampField: string) { + return keyMetric.metrics.reduce((acc, metric) => { + const filter = metric.filter ? getElasticsearchQueryOrThrow(metric.filter) : { match_all: {} }; + const aggs = { metric: buildAggregation(metric, timestampField) }; + return { + ...acc, + [`_${keyMetric.name}_${metric.name}`]: { + filter, + ...(metric.aggregation !== 'doc_count' ? { aggs } : {}), + }, + }; + }, {}); +} + +function buildBucketPath(prefix: string, metric: Metric) { + const { aggregation } = metric; + switch (aggregation) { + case 'doc_count': + return `${prefix}>_count`; + case 'std_deviation': + return `${prefix}>metric[std_deviation]`; + case 'percentile': + return `${prefix}>metric[${metric.percentile}]`; + case 'last_value': + return `${prefix}>metric[${metric.field}]`; + default: + return `${prefix}>metric`; + } +} + +function convertEquationToPainless(bucketsPath: Record, equation: string) { + const workingEquation = equation || Object.keys(bucketsPath).join(' + '); + return Object.keys(bucketsPath).reduce((acc, key) => { + return acc.replaceAll(key, `params.${key}`); + }, workingEquation); +} + +function buildMetricEquation(keyMetric: KeyMetric) { + const bucketsPath = keyMetric.metrics.reduce( + (acc, metric) => ({ + ...acc, + [metric.name]: buildBucketPath(`_${keyMetric.name}_${metric.name}`, metric), + }), + {} + ); + return { + bucket_script: { + buckets_path: bucketsPath, + script: { + source: convertEquationToPainless(bucketsPath, keyMetric.equation), + lang: 'painless', + }, + }, + }; +} + +export function generateMetricAggregations(definition: EntityDefinition) { + if (!definition.metrics) { + return {}; + } + return definition.metrics.reduce((aggs, keyMetric) => { + return { + ...aggs, + ...buildMetricAggregations(keyMetric, definition.timestampField), + [`entity.metric.${keyMetric.name}`]: buildMetricEquation(keyMetric), + }; + }, {}); +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_transform.test.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_transform.test.ts new file mode 100644 index 0000000000000..e97293b77dd4f --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_transform.test.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { entityDefinition } from '../helpers/fixtures/entity_definition'; +import { generateTransform } from './generate_transform'; + +describe('generateTransform(definition)', () => { + it('should generate a valid summary transform', () => { + const transform = generateTransform(entityDefinition); + expect(transform).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_transform.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_transform.ts new file mode 100644 index 0000000000000..6a8c0bd637715 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_transform.ts @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EntityDefinition } from '@kbn/entities-schema'; +import { + QueryDslQueryContainer, + TransformPutTransformRequest, +} from '@elastic/elasticsearch/lib/api/types'; +import { getElasticsearchQueryOrThrow } from '../helpers/get_elasticsearch_query_or_throw'; +import { generateMetricAggregations } from './generate_metric_aggregations'; +import { + ENTITY_BASE_PREFIX, + ENTITY_DEFAULT_FREQUENCY, + ENTITY_DEFAULT_SYNC_DELAY, +} from '../../../../common/constants_entities'; +import { generateMetadataAggregations } from './generate_metadata_aggregations'; +import { generateTransformId } from './generate_transform_id'; +import { generateIngestPipelineId } from '../ingest_pipeline/generate_ingest_pipeline_id'; + +export function generateTransform(definition: EntityDefinition): TransformPutTransformRequest { + const filter: QueryDslQueryContainer[] = [ + { + range: { + [definition.timestampField]: { + gte: `now-${definition.lookback.toJSON()}`, + }, + }, + }, + ]; + + if (definition.filter) { + filter.push(getElasticsearchQueryOrThrow(definition.filter)); + } + + return { + transform_id: generateTransformId(definition), + defer_validation: true, + source: { + index: definition.indexPatterns, + query: { + bool: { + filter, + }, + }, + }, + dest: { + index: `${ENTITY_BASE_PREFIX}.noop`, + pipeline: generateIngestPipelineId(definition), + }, + frequency: definition.settings?.frequency || ENTITY_DEFAULT_FREQUENCY, + sync: { + time: { + field: definition.settings?.syncField ?? definition.timestampField, + delay: definition.settings?.syncDelay ?? ENTITY_DEFAULT_SYNC_DELAY, + }, + }, + settings: { + deduce_mappings: false, + unattended: true, + }, + pivot: { + group_by: definition.identityFields.reduce( + (acc, id) => ({ + ...acc, + [`entity.identity.${id.field}`]: { + terms: { field: id.field, missing_bucket: id.optional }, + }, + }), + {} + ), + aggs: { + ...generateMetricAggregations(definition), + ...generateMetadataAggregations(definition), + 'entity.latestTimestamp': { + max: { + field: definition.timestampField, + }, + }, + }, + }, + }; +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_transform_id.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_transform_id.ts new file mode 100644 index 0000000000000..06faedb916774 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_transform_id.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EntityDefinition } from '@kbn/entities-schema'; +import { ENTITY_TRANSFORM_PREFIX } from '../../../../common/constants_entities'; + +export function generateTransformId(definition: EntityDefinition) { + return `${ENTITY_TRANSFORM_PREFIX}-${definition.id}`; +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/manage_index_templates.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/manage_index_templates.ts index b853d7a360e1d..b364e63ff9a1f 100644 --- a/x-pack/plugins/observability_solution/asset_manager/server/lib/manage_index_templates.ts +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/manage_index_templates.ts @@ -6,6 +6,7 @@ */ import { + ClusterPutComponentTemplateRequest, IndicesGetIndexTemplateResponse, IndicesPutIndexTemplateRequest, } from '@elastic/elasticsearch/lib/api/types'; @@ -47,21 +48,18 @@ function templateExists( }); } -// interface IndexPatternJson { -// index_patterns: string[]; -// name: string; -// template: { -// mappings: Record; -// settings: Record; -// }; -// } - interface TemplateManagementOptions { esClient: ElasticsearchClient; template: IndicesPutIndexTemplateRequest; logger: Logger; } +interface ComponentManagementOptions { + esClient: ElasticsearchClient; + component: ClusterPutComponentTemplateRequest; + logger: Logger; +} + export async function maybeCreateTemplate({ esClient, template, @@ -93,9 +91,6 @@ export async function maybeCreateTemplate({ } export async function upsertTemplate({ esClient, template, logger }: TemplateManagementOptions) { - const pattern = ASSETS_INDEX_PREFIX + '*'; - template.index_patterns = [pattern]; - try { await esClient.indices.putIndexTemplate(template); } catch (error: any) { @@ -108,3 +103,17 @@ export async function upsertTemplate({ esClient, template, logger }: TemplateMan ); logger.debug(`Asset manager index template: ${JSON.stringify(template)}`); } + +export async function upsertComponent({ esClient, component, logger }: ComponentManagementOptions) { + try { + await esClient.cluster.putComponentTemplate(component); + } catch (error: any) { + logger.error(`Error updating asset manager component template: ${error.message}`); + return; + } + + logger.info( + `Asset manager component template is up to date (use debug logging to see what was installed)` + ); + logger.debug(`Asset manager component template: ${JSON.stringify(component)}`); +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/plugin.ts b/x-pack/plugins/observability_solution/asset_manager/server/plugin.ts index 73b2cbe87c138..4c947926cf571 100644 --- a/x-pack/plugins/observability_solution/asset_manager/server/plugin.ts +++ b/x-pack/plugins/observability_solution/asset_manager/server/plugin.ts @@ -15,12 +15,17 @@ import { Logger, } from '@kbn/core/server'; -import { upsertTemplate } from './lib/manage_index_templates'; +import { upsertComponent, upsertTemplate } from './lib/manage_index_templates'; import { setupRoutes } from './routes'; import { assetsIndexTemplateConfig } from './templates/assets_template'; import { AssetClient } from './lib/asset_client'; import { AssetManagerPluginSetupDependencies, AssetManagerPluginStartDependencies } from './types'; import { AssetManagerConfig, configSchema, exposeToBrowserConfig } from '../common/config'; +import { entitiesBaseComponentTemplateConfig } from './templates/components/base'; +import { entitiesEventComponentTemplateConfig } from './templates/components/event'; +import { entitiesIndexTemplateConfig } from './templates/entities_template'; +import { entityDefinition } from './saved_objects'; +import { entitiesEntityComponentTemplateConfig } from './templates/components/entity'; export type AssetManagerServerPluginSetup = ReturnType; export type AssetManagerServerPluginStart = ReturnType; @@ -56,6 +61,8 @@ export class AssetManagerServerPlugin this.logger.info('Server is enabled'); + core.savedObjects.registerType(entityDefinition); + const assetClient = new AssetClient({ sourceIndices: this.config.sourceIndices, getApmIndices: plugins.apmDataAccess.getApmIndices, @@ -63,7 +70,7 @@ export class AssetManagerServerPlugin }); const router = core.http.createRouter(); - setupRoutes({ router, assetClient }); + setupRoutes({ router, assetClient, logger: this.logger }); return { assetClient, @@ -76,12 +83,36 @@ export class AssetManagerServerPlugin return; } + const esClient = core.elasticsearch.client.asInternalUser; upsertTemplate({ - esClient: core.elasticsearch.client.asInternalUser, + esClient, template: assetsIndexTemplateConfig, logger: this.logger, }).catch(() => {}); // it shouldn't reject, but just in case + // Install entities compoent templates and index template + Promise.all([ + upsertComponent({ + esClient, + logger: this.logger, + component: entitiesBaseComponentTemplateConfig, + }), + upsertComponent({ + esClient, + logger: this.logger, + component: entitiesEventComponentTemplateConfig, + }), + upsertComponent({ + esClient, + logger: this.logger, + component: entitiesEntityComponentTemplateConfig, + }), + ]) + .then(() => + upsertTemplate({ esClient, logger: this.logger, template: entitiesIndexTemplateConfig }) + ) + .catch(() => {}); + return {}; } diff --git a/x-pack/plugins/observability_solution/asset_manager/server/routes/entities/create.ts b/x-pack/plugins/observability_solution/asset_manager/server/routes/entities/create.ts new file mode 100644 index 0000000000000..d403c39ae0ed1 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/routes/entities/create.ts @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { RequestHandlerContext } from '@kbn/core/server'; +import { EntityDefinition, entityDefinitionSchema } from '@kbn/entities-schema'; +import { stringifyZodError } from '@kbn/zod-helpers'; +import { SetupRouteOptions } from '../types'; +import { saveEntityDefinition } from '../../lib/entities/save_entity_definition'; +import { createAndInstallIngestPipeline } from '../../lib/entities/create_and_install_ingest_pipeline'; +import { EntityIdConflict } from '../../lib/entities/errors/entity_id_conflict_error'; +import { createAndInstallTransform } from '../../lib/entities/create_and_install_transform'; +import { EntitySecurityException } from '../../lib/entities/errors/entity_security_exception'; +import { InvalidTransformError } from '../../lib/entities/errors/invalid_transform_error'; +import { startTransform } from '../../lib/entities/start_transform'; +import { deleteEntityDefinition } from '../../lib/entities/delete_entity_definition'; +import { deleteIngestPipeline } from '../../lib/entities/delete_ingest_pipeline'; +import { stopAndDeleteTransform } from '../../lib/entities/stop_and_delete_transform'; +import { ENTITY_API_PREFIX } from '../../../common/constants_entities'; + +export function createEntityDefinitionRoute({ + router, + logger, +}: SetupRouteOptions) { + router.post( + { + path: `${ENTITY_API_PREFIX}/definition`, + validate: { + body: (body, res) => { + try { + return res.ok(entityDefinitionSchema.parse(body)); + } catch (e) { + return res.badRequest(stringifyZodError(e)); + } + }, + }, + }, + async (context, req, res) => { + let definitionCreated = false; + let ingestPipelineCreated = false; + let transformCreated = false; + const soClient = (await context.core).savedObjects.client; + const esClient = (await context.core).elasticsearch.client.asCurrentUser; + try { + const definition = await saveEntityDefinition(soClient, req.body); + definitionCreated = true; + await createAndInstallIngestPipeline(esClient, definition, logger); + ingestPipelineCreated = true; + await createAndInstallTransform(esClient, definition, logger); + transformCreated = true; + await startTransform(esClient, definition, logger); + + return res.ok({ body: definition }); + } catch (e) { + // Clean up anything that was successful. + if (definitionCreated) { + await deleteEntityDefinition(soClient, req.body, logger); + } + if (ingestPipelineCreated) { + await deleteIngestPipeline(esClient, req.body, logger); + } + if (transformCreated) { + await stopAndDeleteTransform(esClient, req.body, logger); + } + if (e instanceof EntityIdConflict) { + return res.conflict({ body: e }); + } + if (e instanceof EntitySecurityException || e instanceof InvalidTransformError) { + return res.customError({ body: e, statusCode: 400 }); + } + return res.customError({ body: e, statusCode: 500 }); + } + } + ); +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/routes/entities/delete.ts b/x-pack/plugins/observability_solution/asset_manager/server/routes/entities/delete.ts new file mode 100644 index 0000000000000..e1b273780a64f --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/routes/entities/delete.ts @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { RequestHandlerContext } from '@kbn/core/server'; +import { schema } from '@kbn/config-schema'; +import { SetupRouteOptions } from '../types'; +import { EntitySecurityException } from '../../lib/entities/errors/entity_security_exception'; +import { InvalidTransformError } from '../../lib/entities/errors/invalid_transform_error'; +import { readEntityDefinition } from '../../lib/entities/read_entity_definition'; +import { stopAndDeleteTransform } from '../../lib/entities/stop_and_delete_transform'; +import { deleteIngestPipeline } from '../../lib/entities/delete_ingest_pipeline'; +import { deleteEntityDefinition } from '../../lib/entities/delete_entity_definition'; +import { EntityDefinitionNotFound } from '../../lib/entities/errors/entity_not_found'; +import { ENTITY_API_PREFIX } from '../../../common/constants_entities'; + +export function deleteEntityDefinitionRoute({ + router, + logger, +}: SetupRouteOptions) { + router.delete<{ id: string }, unknown, unknown>( + { + path: `${ENTITY_API_PREFIX}/definition/{id}`, + validate: { + params: schema.object({ + id: schema.string(), + }), + }, + }, + async (context, req, res) => { + try { + const soClient = (await context.core).savedObjects.client; + const esClient = (await context.core).elasticsearch.client.asCurrentUser; + + const definition = await readEntityDefinition(soClient, req.params.id, logger); + await stopAndDeleteTransform(esClient, definition, logger); + await deleteIngestPipeline(esClient, definition, logger); + await deleteEntityDefinition(soClient, definition, logger); + + return res.ok({ body: { acknowledged: true } }); + } catch (e) { + if (e instanceof EntityDefinitionNotFound) { + return res.notFound({ body: e }); + } + if (e instanceof EntitySecurityException || e instanceof InvalidTransformError) { + return res.customError({ body: e, statusCode: 400 }); + } + return res.customError({ body: e, statusCode: 500 }); + } + } + ); +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/routes/entities/reset.ts b/x-pack/plugins/observability_solution/asset_manager/server/routes/entities/reset.ts new file mode 100644 index 0000000000000..3109ffc44520f --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/routes/entities/reset.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { RequestHandlerContext } from '@kbn/core/server'; +import { schema } from '@kbn/config-schema'; +import { SetupRouteOptions } from '../types'; +import { EntitySecurityException } from '../../lib/entities/errors/entity_security_exception'; +import { InvalidTransformError } from '../../lib/entities/errors/invalid_transform_error'; +import { readEntityDefinition } from '../../lib/entities/read_entity_definition'; +import { stopAndDeleteTransform } from '../../lib/entities/stop_and_delete_transform'; +import { deleteIngestPipeline } from '../../lib/entities/delete_ingest_pipeline'; +import { deleteIndex } from '../../lib/entities/delete_index'; +import { createAndInstallIngestPipeline } from '../../lib/entities/create_and_install_ingest_pipeline'; +import { createAndInstallTransform } from '../../lib/entities/create_and_install_transform'; +import { startTransform } from '../../lib/entities/start_transform'; +import { EntityDefinitionNotFound } from '../../lib/entities/errors/entity_not_found'; +import { ENTITY_API_PREFIX } from '../../../common/constants_entities'; + +export function resetEntityDefinitionRoute({ + router, + logger, +}: SetupRouteOptions) { + router.post<{ id: string }, unknown, unknown>( + { + path: `${ENTITY_API_PREFIX}/definition/{id}/_reset`, + validate: { + params: schema.object({ + id: schema.string(), + }), + }, + }, + async (context, req, res) => { + try { + const soClient = (await context.core).savedObjects.client; + const esClient = (await context.core).elasticsearch.client.asCurrentUser; + + const definition = await readEntityDefinition(soClient, req.params.id, logger); + + // Delete the transform and ingest pipeline + await stopAndDeleteTransform(esClient, definition, logger); + await deleteIngestPipeline(esClient, definition, logger); + await deleteIndex(esClient, definition, logger); + + // Recreate everything + await createAndInstallIngestPipeline(esClient, definition, logger); + await createAndInstallTransform(esClient, definition, logger); + await startTransform(esClient, definition, logger); + + return res.ok({ body: { acknowledged: true } }); + } catch (e) { + if (e instanceof EntityDefinitionNotFound) { + return res.notFound({ body: e }); + } + if (e instanceof EntitySecurityException || e instanceof InvalidTransformError) { + return res.customError({ body: e, statusCode: 400 }); + } + return res.customError({ body: e, statusCode: 500 }); + } + } + ); +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/routes/index.ts b/x-pack/plugins/observability_solution/asset_manager/server/routes/index.ts index 52d3198bb8a9f..d0b6c9f7ff0f1 100644 --- a/x-pack/plugins/observability_solution/asset_manager/server/routes/index.ts +++ b/x-pack/plugins/observability_solution/asset_manager/server/routes/index.ts @@ -14,16 +14,23 @@ import { hostsRoutes } from './assets/hosts'; import { servicesRoutes } from './assets/services'; import { containersRoutes } from './assets/containers'; import { podsRoutes } from './assets/pods'; +import { createEntityDefinitionRoute } from './entities/create'; +import { deleteEntityDefinitionRoute } from './entities/delete'; +import { resetEntityDefinitionRoute } from './entities/reset'; export function setupRoutes({ router, assetClient, + logger, }: SetupRouteOptions) { - pingRoute({ router, assetClient }); - sampleAssetsRoutes({ router, assetClient }); - assetsRoutes({ router, assetClient }); - hostsRoutes({ router, assetClient }); - servicesRoutes({ router, assetClient }); - containersRoutes({ router, assetClient }); - podsRoutes({ router, assetClient }); + pingRoute({ router, assetClient, logger }); + sampleAssetsRoutes({ router, assetClient, logger }); + assetsRoutes({ router, assetClient, logger }); + hostsRoutes({ router, assetClient, logger }); + servicesRoutes({ router, assetClient, logger }); + containersRoutes({ router, assetClient, logger }); + podsRoutes({ router, assetClient, logger }); + createEntityDefinitionRoute({ router, assetClient, logger }); + deleteEntityDefinitionRoute({ router, assetClient, logger }); + resetEntityDefinitionRoute({ router, assetClient, logger }); } diff --git a/x-pack/plugins/observability_solution/asset_manager/server/routes/types.ts b/x-pack/plugins/observability_solution/asset_manager/server/routes/types.ts index ae1b967a5b596..561819d18cdae 100644 --- a/x-pack/plugins/observability_solution/asset_manager/server/routes/types.ts +++ b/x-pack/plugins/observability_solution/asset_manager/server/routes/types.ts @@ -6,9 +6,11 @@ */ import { IRouter, RequestHandlerContextBase } from '@kbn/core-http-server'; +import { Logger } from '@kbn/core/server'; import { AssetClient } from '../lib/asset_client'; export interface SetupRouteOptions { router: IRouter; assetClient: AssetClient; + logger: Logger; } diff --git a/x-pack/plugins/observability_solution/asset_manager/server/saved_objects/entity_definition.ts b/x-pack/plugins/observability_solution/asset_manager/server/saved_objects/entity_definition.ts new file mode 100644 index 0000000000000..5a63444974ad7 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/saved_objects/entity_definition.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 { SavedObject, SavedObjectsType } from '@kbn/core/server'; +import { EntityDefinition } from '@kbn/entities-schema'; + +export const SO_ENTITY_DEFINITION_TYPE = 'entity-definition'; + +export const entityDefinition: SavedObjectsType = { + name: SO_ENTITY_DEFINITION_TYPE, + hidden: false, + namespaceType: 'multiple-isolated', + mappings: { + dynamic: false, + properties: { + id: { type: 'keyword' }, + name: { type: 'text' }, + description: { type: 'text' }, + type: { type: 'keyword' }, + filter: { type: 'keyword' }, + indexPatterns: { type: 'keyword' }, + identityFields: { type: 'object' }, + categories: { type: 'keyword' }, + metadata: { type: 'object' }, + metrics: { type: 'object' }, + staticFields: { type: 'object' }, + }, + }, + management: { + displayName: 'Entity Definition', + importableAndExportable: false, + getTitle(sloSavedObject: SavedObject) { + return `EntityDefinition: [${sloSavedObject.attributes.name}]`; + }, + }, +}; diff --git a/x-pack/plugins/observability_solution/asset_manager/server/saved_objects/index.ts b/x-pack/plugins/observability_solution/asset_manager/server/saved_objects/index.ts new file mode 100644 index 0000000000000..6145b05438bb2 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/saved_objects/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { entityDefinition, SO_ENTITY_DEFINITION_TYPE } from './entity_definition'; diff --git a/x-pack/plugins/observability_solution/asset_manager/server/templates/assets_template.ts b/x-pack/plugins/observability_solution/asset_manager/server/templates/assets_template.ts index 71d4058eba4c0..b99ecc4559187 100644 --- a/x-pack/plugins/observability_solution/asset_manager/server/templates/assets_template.ts +++ b/x-pack/plugins/observability_solution/asset_manager/server/templates/assets_template.ts @@ -6,11 +6,13 @@ */ import { IndicesPutIndexTemplateRequest } from '@elastic/elasticsearch/lib/api/types'; +import { ASSETS_INDEX_PREFIX } from '../constants'; export const assetsIndexTemplateConfig: IndicesPutIndexTemplateRequest = { name: 'assets', priority: 100, data_stream: {}, + index_patterns: [`${ASSETS_INDEX_PREFIX}*`], template: { settings: {}, mappings: { diff --git a/x-pack/plugins/observability_solution/asset_manager/server/templates/components/base.ts b/x-pack/plugins/observability_solution/asset_manager/server/templates/components/base.ts new file mode 100644 index 0000000000000..adf527d653d9c --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/templates/components/base.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ClusterPutComponentTemplateRequest } from '@elastic/elasticsearch/lib/api/types'; + +export const entitiesBaseComponentTemplateConfig: ClusterPutComponentTemplateRequest = { + name: 'entities_v1_base', + _meta: { + documentation: 'https://www.elastic.co/guide/en/ecs/current/ecs-base.html', + ecs_version: '8.0.0', + }, + template: { + mappings: { + properties: { + '@timestamp': { + type: 'date', + }, + labels: { + type: 'object', + }, + tags: { + ignore_above: 1024, + type: 'keyword', + }, + }, + }, + }, +}; diff --git a/x-pack/plugins/observability_solution/asset_manager/server/templates/components/entity.ts b/x-pack/plugins/observability_solution/asset_manager/server/templates/components/entity.ts new file mode 100644 index 0000000000000..e696d32e0dfb2 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/templates/components/entity.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 { ClusterPutComponentTemplateRequest } from '@elastic/elasticsearch/lib/api/types'; + +export const entitiesEntityComponentTemplateConfig: ClusterPutComponentTemplateRequest = { + name: 'entities_v1_entity', + _meta: { + ecs_version: '8.0.0', + }, + template: { + mappings: { + properties: { + entity: { + properties: { + id: { + ignore_above: 1024, + type: 'keyword', + }, + indexPatterns: { + ignore_above: 1024, + type: 'keyword', + }, + defintionId: { + ignore_above: 1024, + type: 'keyword', + }, + latestTimestamp: { + type: 'date', + }, + }, + }, + }, + }, + }, +}; diff --git a/x-pack/plugins/observability_solution/asset_manager/server/templates/components/event.ts b/x-pack/plugins/observability_solution/asset_manager/server/templates/components/event.ts new file mode 100644 index 0000000000000..6ad4b628fdf36 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/templates/components/event.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ClusterPutComponentTemplateRequest } from '@elastic/elasticsearch/lib/api/types'; + +export const entitiesEventComponentTemplateConfig: ClusterPutComponentTemplateRequest = { + name: 'entities_v1_event', + _meta: { + documentation: 'https://www.elastic.co/guide/en/ecs/current/ecs-event.html', + ecs_version: '8.0.0', + }, + template: { + mappings: { + properties: { + event: { + properties: { + ingested: { + type: 'date', + }, + }, + }, + }, + }, + }, +}; diff --git a/x-pack/plugins/observability_solution/asset_manager/server/templates/entities_template.ts b/x-pack/plugins/observability_solution/asset_manager/server/templates/entities_template.ts new file mode 100644 index 0000000000000..d3934b24ea82c --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/templates/entities_template.ts @@ -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 { IndicesPutIndexTemplateRequest } from '@elastic/elasticsearch/lib/api/types'; +import { ENTITY_BASE_PREFIX } from '../../common/constants_entities'; + +export const entitiesIndexTemplateConfig: IndicesPutIndexTemplateRequest = { + name: 'entities_v1_index_template', + _meta: { + description: 'The entities index template', + ecs_version: '8.0.0', + }, + composed_of: ['entities_v1_base', 'entities_v1_event', 'entities_v1_entity'], + index_patterns: [`${ENTITY_BASE_PREFIX}.*`], + priority: 1, + template: { + mappings: { + _meta: { + version: '1.6.0', + }, + date_detection: false, + dynamic_templates: [ + { + strings_as_keyword: { + mapping: { + ignore_above: 1024, + type: 'keyword', + }, + match_mapping_type: 'string', + }, + }, + { + entity_metrics: { + mapping: { + // @ts-expect-error this should work per: https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic-templates.html#match-mapping-type + type: '{dynamic_type}', + }, + // @ts-expect-error this should work per: https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic-templates.html#match-mapping-type + match_mapping_type: ['long', 'double'], + path_match: 'entity.metric.*', + }, + }, + ], + }, + settings: { + index: { + codec: 'best_compression', + mapping: { + total_fields: { + limit: 2000, + }, + }, + }, + }, + }, +}; diff --git a/x-pack/plugins/observability_solution/asset_manager/tsconfig.json b/x-pack/plugins/observability_solution/asset_manager/tsconfig.json index da1095d989afb..dbbc36252bfb1 100644 --- a/x-pack/plugins/observability_solution/asset_manager/tsconfig.json +++ b/x-pack/plugins/observability_solution/asset_manager/tsconfig.json @@ -26,6 +26,9 @@ "@kbn/metrics-data-access-plugin", "@kbn/core-elasticsearch-server", "@kbn/core-saved-objects-api-server", - "@kbn/core-saved-objects-api-server-mocks" + "@kbn/core-saved-objects-api-server-mocks", + "@kbn/entities-schema", + "@kbn/es-query", + "@kbn/zod-helpers" ] } diff --git a/x-pack/plugins/observability_solution/infra/common/alerting/metrics/alert_link.ts b/x-pack/plugins/observability_solution/infra/common/alerting/metrics/alert_link.ts index ff6b6d2c752ce..a1f0da3531215 100644 --- a/x-pack/plugins/observability_solution/infra/common/alerting/metrics/alert_link.ts +++ b/x-pack/plugins/observability_solution/infra/common/alerting/metrics/alert_link.ts @@ -9,7 +9,7 @@ import { ALERT_RULE_PARAMETERS, TIMESTAMP } from '@kbn/rule-data-utils'; import { encode } from '@kbn/rison'; import { stringify } from 'query-string'; import { ParsedTechnicalFields } from '@kbn/rule-registry-plugin/common/parse_technical_fields'; -import { InventoryItemType } from '@kbn/metrics-data-access-plugin/common'; +import type { InventoryItemType } from '@kbn/metrics-data-access-plugin/common'; import { fifteenMinutesInMilliseconds, HOST_FIELD, @@ -59,37 +59,36 @@ export const getInventoryViewInAppUrl = ( if (nodeType) { if (hostName) { return getLinkToHostDetails({ hostName, timestamp: inventoryFields[TIMESTAMP] }); - } else { - const linkToParams = { - nodeType: inventoryFields[nodeTypeField][0], - timestamp: Date.parse(inventoryFields[TIMESTAMP]), - customMetric: '', - metric: '', - }; + } + const linkToParams = { + nodeType: inventoryFields[nodeTypeField][0], + timestamp: Date.parse(inventoryFields[TIMESTAMP]), + customMetric: '', + metric: '', + }; - // We always pick the first criteria metric for the URL - const criteriaMetric = inventoryFields[`${ALERT_RULE_PARAMETERS}.criteria.metric`][0]; - if (criteriaMetric === 'custom') { - const criteriaCustomMetricId = - inventoryFields[`${ALERT_RULE_PARAMETERS}.criteria.customMetric.id`][0]; - const criteriaCustomMetricAggregation = - inventoryFields[`${ALERT_RULE_PARAMETERS}.criteria.customMetric.aggregation`][0]; - const criteriaCustomMetricField = - inventoryFields[`${ALERT_RULE_PARAMETERS}.criteria.customMetric.field`][0]; + // We always pick the first criteria metric for the URL + const criteriaMetric = inventoryFields[`${ALERT_RULE_PARAMETERS}.criteria.metric`][0]; + if (criteriaMetric === 'custom') { + const criteriaCustomMetricId = + inventoryFields[`${ALERT_RULE_PARAMETERS}.criteria.customMetric.id`][0]; + const criteriaCustomMetricAggregation = + inventoryFields[`${ALERT_RULE_PARAMETERS}.criteria.customMetric.aggregation`][0]; + const criteriaCustomMetricField = + inventoryFields[`${ALERT_RULE_PARAMETERS}.criteria.customMetric.field`][0]; - const customMetric = encode({ - id: criteriaCustomMetricId, - type: 'custom', - field: criteriaCustomMetricField, - aggregation: criteriaCustomMetricAggregation, - }); - linkToParams.customMetric = customMetric; - linkToParams.metric = customMetric; - } else { - linkToParams.metric = encode({ type: criteriaMetric }); - } - return `${LINK_TO_INVENTORY}?${stringify(linkToParams)}`; + const customMetric = encode({ + id: criteriaCustomMetricId, + type: 'custom', + field: criteriaCustomMetricField, + aggregation: criteriaCustomMetricAggregation, + }); + linkToParams.customMetric = customMetric; + linkToParams.metric = customMetric; + } else { + linkToParams.metric = encode({ type: criteriaMetric }); } + return `${LINK_TO_INVENTORY}?${stringify(linkToParams)}`; } return LINK_TO_INVENTORY; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/link_to/redirect_to_inventory.tsx b/x-pack/plugins/observability_solution/infra/public/pages/link_to/redirect_to_inventory.tsx index 37ddbacf72488..c6d0bed51ed9c 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/link_to/redirect_to_inventory.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/link_to/redirect_to_inventory.tsx @@ -5,43 +5,32 @@ * 2.0. */ -import React from 'react'; +import React, { useEffect } from 'react'; import { parse } from 'query-string'; -import { Redirect, RouteComponentProps } from 'react-router-dom'; - -// FIXME what would be the right way to build this query string? -const QUERY_STRING_TEMPLATE = - "?waffleFilter=(expression:'',kind:kuery)&waffleTime=(currentTime:{timestamp},isAutoReloading:!f)&waffleOptions=(accountId:'',autoBounds:!t,boundsOverride:(max:1,min:0),customMetrics:!({customMetric}),customOptions:!(),groupBy:!(),legend:(palette:cool,reverseColors:!f,steps:10),metric:{metric},nodeType:{nodeType},region:'',sort:(by:name,direction:desc),timelineOpen:!f,view:map)"; +import { RouteComponentProps } from 'react-router-dom'; +import type { SerializableRecord } from '@kbn/utility-types'; +import { INVENTORY_LOCATOR_ID } from '@kbn/observability-shared-plugin/public'; +import { useKibanaContextForPlugin } from '../../hooks/use_kibana'; export const RedirectToInventory: React.FC = ({ location }) => { - const parsedQueryString = parseQueryString(location.search); - - const inventoryQueryString = QUERY_STRING_TEMPLATE.replace( - /{(\w+)}/g, - (_, key) => parsedQueryString[key] || '' - ); - - return ; + const { + services: { share }, + } = useKibanaContextForPlugin(); + const baseLocator = share.url.locators.get(INVENTORY_LOCATOR_ID); + + useEffect(() => { + const parsedQueryString = parse(location.search || '', { sort: false }); + const currentTime = parseFloat((parsedQueryString.timestamp ?? '') as string); + + baseLocator?.navigate({ + ...parsedQueryString, + waffleTime: { + currentTime, + isAutoReloading: false, + }, + state: location.state as SerializableRecord, + }); + }, [baseLocator, location.search, location.state]); + + return null; }; - -function parseQueryString(search: string): Record { - if (search.length === 0) { - return {}; - } - - const obj = parse(search.substring(1)); - - // Force all values into string. If they are empty don't create the keys - for (const key in obj) { - if (Object.hasOwnProperty.call(obj, key)) { - if (!obj[key]) { - delete obj[key]; - } - if (Array.isArray(obj.key)) { - obj[key] = obj[key]![0]; - } - } - } - - return obj as Record; -} diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/get_dataset_info/get_relevant_field_names.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/get_dataset_info/get_relevant_field_names.ts index 543641098836f..2f32731ac3f2d 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/get_dataset_info/get_relevant_field_names.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/get_dataset_info/get_relevant_field_names.ts @@ -36,9 +36,25 @@ export async function getRelevantFieldNames({ }): Promise<{ fields: string[] }> { const dataViewsService = await dataViews.dataViewsServiceFactory(savedObjectsClient, esClient); + const hasAnyHitsResponse = await esClient.search({ + index, + _source: false, + track_total_hits: 1, + terminate_after: 1, + }); + + const hitCount = + typeof hasAnyHitsResponse.hits.total === 'number' + ? hasAnyHitsResponse.hits.total + : hasAnyHitsResponse.hits.total?.value ?? 0; + + // all fields are empty in this case, so get them all + const includeEmptyFields = hitCount === 0; + const fields = await dataViewsService.getFieldsForWildcard({ pattern: castArray(index).join(','), allowNoIndex: true, + includeEmptyFields, indexFilter: start && end ? { @@ -74,7 +90,7 @@ export async function getRelevantFieldNames({ const relevantFields = await Promise.all( chunk(fieldNames, 500).map(async (fieldsInChunk) => { const chunkResponse$ = ( - await chat('get_relevent_dataset_names', { + await chat('get_relevant_dataset_names', { signal, messages: [ { @@ -88,7 +104,8 @@ export async function getRelevantFieldNames({ CIRCUMSTANCES include fields not mentioned in this list.`, }, }, - ...messages.slice(1), + // remove the system message and the function request + ...messages.slice(1, -1), { '@timestamp': new Date().toISOString(), message: { diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/get_dataset_info/index.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/get_dataset_info/index.ts index e5b4e21195003..f016ae126ca53 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/get_dataset_info/index.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/get_dataset_info/index.ts @@ -26,7 +26,6 @@ export function registerGetDatasetInfoFunction({ 'This function allows the assistant to get information about available indices and their fields.', parameters: { type: 'object', - additionalProperties: false, properties: { index: { type: 'string', @@ -90,7 +89,7 @@ export function registerGetDatasetInfoFunction({ return { content: { indices: [index], - fields: relevantFieldNames, + fields: relevantFieldNames.fields, }, }; } diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/nav_control/index.tsx b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/nav_control/index.tsx index cf8a5cd9dffe7..8757b9f8235bf 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/nav_control/index.tsx +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/nav_control/index.tsx @@ -6,7 +6,7 @@ */ import React, { useEffect, useRef, useState } from 'react'; import { AssistantAvatar, useAbortableAsync } from '@kbn/observability-ai-assistant-plugin/public'; -import { EuiButton, EuiLoadingSpinner } from '@elastic/eui'; +import { EuiButton, EuiLoadingSpinner, EuiToolTip } from '@elastic/eui'; import { css } from '@emotion/react'; import { v4 } from 'uuid'; import useObservable from 'react-use/lib/useObservable'; @@ -91,31 +91,46 @@ export function NavControl({}: {}) { } `; + useEffect(() => { + const keyboardListener = (event: KeyboardEvent) => { + if (event.ctrlKey && event.code === 'Semicolon') { + service.conversations.openNewConversation({ + messages: [], + }); + } + }; + + window.addEventListener('keypress', keyboardListener); + + return () => { + window.removeEventListener('keypress', keyboardListener); + }; + }, [service.conversations]); + if (!isVisible) { return null; } return ( <> - { - service.conversations.openNewConversation({ - messages: [], - }); - }} - color="primary" - size="s" - fullWidth={false} - minWidth={0} - > - {chatService.loading ? : } - + + { + service.conversations.openNewConversation({ + messages: [], + }); + }} + color="primary" + size="s" + fullWidth={false} + minWidth={0} + > + {chatService.loading ? : } + + {chatService.value ? ( ); } + +const buttonLabel = i18n.translate( + 'xpack.observabilityAiAssistant.navControl.openTheAIAssistantPopoverLabel', + { defaultMessage: 'Open the AI Assistant' } +); diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/alerts.ts b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/alerts.ts index 7b62ca4f5a6d2..03c61843e702a 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/alerts.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/alerts.ts @@ -6,18 +6,49 @@ */ import datemath from '@elastic/datemath'; +import { KibanaRequest } from '@kbn/core/server'; import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; +import { FunctionVisibility } from '@kbn/observability-ai-assistant-plugin/common'; +import { getRelevantFieldNames } from '@kbn/observability-ai-assistant-plugin/server/functions/get_dataset_info/get_relevant_field_names'; import { ParsedTechnicalFields } from '@kbn/rule-registry-plugin/common'; import { ALERT_STATUS, ALERT_STATUS_ACTIVE, } from '@kbn/rule-registry-plugin/common/technical_rule_data_field_names'; import { omit } from 'lodash'; -import { KibanaRequest } from '@kbn/core/server'; import { FunctionRegistrationParameters } from '.'; -const OMITTED_ALERT_FIELDS = [ +const defaultFields = [ + '@timestamp', + 'kibana.alert.start', + 'kibana.alert.end', + 'kibana.alert.flapping', + 'kibana.alert.group', + 'kibana.alert.instance.id', + 'kibana.alert.reason', + 'kibana.alert.rule.category', + 'kibana.alert.rule.name', + 'kibana.alert.rule.tags', + 'kibana.alert.start', + 'kibana.alert.status', + 'kibana.alert.time_range.gte', + 'kibana.alert.time_range.lte', + 'kibana.alert.workflow_status', 'tags', + // infra + 'host.name', + 'container.id', + 'kubernetes.pod.name', + // APM + 'processor.event', + 'service.environment', + 'service.name', + 'service.node.name', + 'transaction.type', + 'transaction.name', +]; + +const OMITTED_ALERT_FIELDS = [ 'event.action', 'event.kind', 'kibana.alert.rule.execution.uuid', @@ -46,22 +77,75 @@ export function registerAlertsFunction({ }: FunctionRegistrationParameters) { functions.registerFunction( { - name: 'alerts', - description: - 'Get alerts for Observability. Display the response in tabular format if appropriate.', - descriptionForUser: 'Get alerts for Observability', + name: 'get_alerts_dataset_info', + visibility: FunctionVisibility.AssistantOnly, + description: `Use this function to get information about alerts data.`, parameters: { type: 'object', properties: { - featureIds: { - type: 'array', - items: { - type: 'string', - enum: DEFAULT_FEATURE_IDS, - }, + start: { + type: 'string', description: - 'The Observability apps for which to retrieve alerts. By default it will return alerts for all apps.', + 'The start of the current time range, in datemath, like now-24h or an ISO timestamp', }, + end: { + type: 'string', + description: + 'The end of the current time range, in datemath, like now-24h or an ISO timestamp', + }, + }, + } as const, + }, + async ( + { arguments: { start, end }, chat, messages }, + signal + ): Promise<{ + content: { + fields: string[]; + }; + }> => { + const core = await resources.context.core; + + const { fields } = await getRelevantFieldNames({ + index: `.alerts-observability*`, + messages, + esClient: core.elasticsearch.client.asInternalUser, + dataViews: await resources.plugins.dataViews.start(), + savedObjectsClient: core.savedObjects.client, + signal, + chat: ( + operationName, + { messages: nextMessages, functionCall, functions: nextFunctions } + ) => { + return chat(operationName, { + messages: nextMessages, + functionCall, + functions: nextFunctions, + signal, + }); + }, + }); + + return { + content: { + fields: fields.length === 0 ? defaultFields : fields, + }, + }; + } + ); + + functions.registerFunction( + { + name: 'alerts', + description: `Get alerts for Observability. Make sure get_alerts_dataset_info was called before. + Use this to get open (and optionally recovered) alerts for Observability assets, like services, + hosts or containers. + Display the response in tabular format if appropriate. + `, + descriptionForUser: 'Get alerts for Observability', + parameters: { + type: 'object', + properties: { start: { type: 'string', description: 'The start of the time range, in Elasticsearch date math, like `now`.', @@ -72,8 +156,7 @@ export function registerAlertsFunction({ }, kqlFilter: { type: 'string', - description: - 'a KQL query to filter the data by. If no filter should be applied, leave it empty.', + description: `Filter alerts by field:value pairs`, }, includeRecovered: { type: 'boolean', @@ -85,15 +168,7 @@ export function registerAlertsFunction({ } as const, }, async ( - { - arguments: { - start: startAsDatemath, - end: endAsDatemath, - featureIds, - filter, - includeRecovered, - }, - }, + { arguments: { start: startAsDatemath, end: endAsDatemath, filter, includeRecovered } }, signal ) => { const alertsClient = await pluginsStart.ruleRegistry.getRacClientWithRequest( @@ -106,10 +181,7 @@ export function registerAlertsFunction({ const kqlQuery = !filter ? [] : [toElasticsearchQuery(fromKueryExpression(filter))]; const response = await alertsClient.find({ - featureIds: - !!featureIds && !!featureIds.length - ? featureIds - : (DEFAULT_FEATURE_IDS as unknown as string[]), + featureIds: DEFAULT_FEATURE_IDS as unknown as string[], query: { bool: { filter: [ @@ -134,6 +206,7 @@ export function registerAlertsFunction({ ], }, }, + size: 10, }); // trim some fields diff --git a/x-pack/plugins/observability_solution/observability_onboarding/server/routes/logs/route.ts b/x-pack/plugins/observability_solution/observability_onboarding/server/routes/logs/route.ts index c7a411441658e..b46b1508ed21b 100644 --- a/x-pack/plugins/observability_solution/observability_onboarding/server/routes/logs/route.ts +++ b/x-pack/plugins/observability_solution/observability_onboarding/server/routes/logs/route.ts @@ -58,12 +58,12 @@ const installShipperSetupRoute = createObservabilityOnboardingServerRoute({ core.setup.http.basePath.publicBaseUrl ?? // priority given to server.publicBaseUrl plugins.cloud?.setup?.kibanaUrl ?? // then cloud id getFallbackKibanaUrl(coreStart); // falls back to local network binding - const installScriptPath = coreStart.http.staticAssets.getPluginAssetHref( - 'standalone_agent_setup.sh' - ); + const scriptDownloadUrl = new URL( + coreStart.http.staticAssets.getPluginAssetHref('standalone_agent_setup.sh'), + kibanaUrl + ).toString(); - const scriptDownloadUrl = `${kibanaUrl}${installScriptPath}`; - const apiEndpoint = `${kibanaUrl}/internal/observability_onboarding`; + const apiEndpoint = new URL(`${kibanaUrl}/internal/observability_onboarding`).toString(); return { apiEndpoint, diff --git a/x-pack/plugins/observability_solution/observability_shared/public/index.ts b/x-pack/plugins/observability_solution/observability_shared/public/index.ts index 93094c79369a9..a06f086d6588d 100644 --- a/x-pack/plugins/observability_solution/observability_shared/public/index.ts +++ b/x-pack/plugins/observability_solution/observability_shared/public/index.ts @@ -103,6 +103,7 @@ export { export { BottomBarActions } from './components/bottom_bar_actions/bottom_bar_actions'; export { FieldValueSelection, FieldValueSuggestions } from './components'; export { ASSET_DETAILS_FLYOUT_LOCATOR_ID } from './locators/infra/asset_details_flyout_locator'; +export { INVENTORY_LOCATOR_ID } from './locators/infra/inventory_locator'; export { ASSET_DETAILS_LOCATOR_ID, type AssetDetailsLocatorParams, diff --git a/x-pack/plugins/observability_solution/observability_shared/public/locators/infra/inventory_locator.ts b/x-pack/plugins/observability_solution/observability_shared/public/locators/infra/inventory_locator.ts new file mode 100644 index 0000000000000..ca6e997468b5b --- /dev/null +++ b/x-pack/plugins/observability_solution/observability_shared/public/locators/infra/inventory_locator.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import type { SerializableRecord } from '@kbn/utility-types'; +import rison from '@kbn/rison'; +import { LocatorDefinition, LocatorPublic } from '@kbn/share-plugin/common'; +import querystring from 'querystring'; + +export type InventoryLocator = LocatorPublic; + +export interface InventoryLocatorParams extends SerializableRecord { + inventoryViewId?: string; + waffleFilter?: { + expression: string; + kind: string; + }; + waffleTime?: { + currentTime: number; + isAutoReloading: boolean; + }; + waffleOptions?: { + accountId: string; + autoBounds: boolean; + boundsOverride: { + max: number; + min: number; + }; + }; + customMetrics?: string; // encoded value + customOptions?: string; // encoded value + groupBy?: { field: string }; + legend?: { + palette: string; + reverseColors: boolean; + steps: number; + }; + metric: string; // encoded value + nodeType: string; + region?: string; + sort: { + by: string; + direction: 'desc' | 'async'; + }; + timelineOpen: boolean; + view: 'map' | 'table'; + state?: SerializableRecord; +} + +export const INVENTORY_LOCATOR_ID = 'INVENTORY_LOCATOR'; + +export class InventoryLocatorDefinition implements LocatorDefinition { + public readonly id = INVENTORY_LOCATOR_ID; + + public readonly getLocation = async (params: InventoryLocatorParams) => { + const paramsWithDefaults = { + waffleFilter: rison.encodeUnknown(params.waffleFilter ?? { kind: 'kuery', expression: '' }), + waffleTime: rison.encodeUnknown( + params.waffleTime ?? { + currentTime: new Date().getTime(), + isAutoReloading: false, + } + ), + waffleOptions: rison.encodeUnknown( + params.waffleOptions ?? { + accountId: '', + autoBounds: true, + boundsOverride: { max: 1, min: 0 }, + } + ), + customMetrics: params.customMetrics, + customOptions: params.customOptions, + groupBy: rison.encodeUnknown(params.groupBy ?? {}), + legend: rison.encodeUnknown( + params.legend ?? { palette: 'cool', reverseColors: false, steps: 10 } + ), + metric: params.metric, + nodeType: rison.encodeUnknown(params.nodeType), + region: rison.encodeUnknown(params.region ?? ''), + sort: rison.encodeUnknown(params.sort ?? { by: 'name', direction: 'desc' }), + timelineOpen: rison.encodeUnknown(params.timelineOpen ?? false), + view: rison.encodeUnknown(params.view ?? 'map'), + }; + + const queryStringParams = querystring.stringify(paramsWithDefaults); + return { + app: 'metrics', + path: `/inventory?${queryStringParams}`, + state: params.state ? params.state : {}, + }; + }; +} diff --git a/x-pack/plugins/observability_solution/observability_shared/public/locators/infra/locators.test.ts b/x-pack/plugins/observability_solution/observability_shared/public/locators/infra/locators.test.ts index ff02eff6dc76f..c7b5e16625e03 100644 --- a/x-pack/plugins/observability_solution/observability_shared/public/locators/infra/locators.test.ts +++ b/x-pack/plugins/observability_solution/observability_shared/public/locators/infra/locators.test.ts @@ -9,6 +9,8 @@ import rison from '@kbn/rison'; import { AssetDetailsLocatorDefinition } from './asset_details_locator'; import { AssetDetailsFlyoutLocatorDefinition } from './asset_details_flyout_locator'; import { HostsLocatorDefinition } from './hosts_locator'; +import { InventoryLocatorDefinition } from './inventory_locator'; +import querystring from 'querystring'; const setupAssetDetailsLocator = async () => { const assetDetailsLocator = new AssetDetailsLocatorDefinition(); @@ -28,6 +30,14 @@ const setupHostsLocator = async () => { }; }; +const setupInventoryLocator = async () => { + const inventoryLocator = new InventoryLocatorDefinition(); + + return { + inventoryLocator, + }; +}; + describe('Infra Locators', () => { describe('Asset Details Locator', () => { const params = { @@ -162,4 +172,59 @@ describe('Infra Locators', () => { expect(Object.keys(state)).toHaveLength(0); }); }); + + describe('Inventory Locator', () => { + const params = { + waffleFilter: { kind: 'kuery', expression: '' }, + waffleTime: { + currentTime: 1715688477985, + isAutoReloading: false, + }, + waffleOptions: { + accountId: '', + autoBounds: true, + boundsOverride: { max: 1, min: 0 }, + }, + customMetrics: undefined, + customOptions: undefined, + groupBy: { field: 'cloud.provider' }, + legend: { palette: 'cool', reverseColors: false, steps: 10 }, + metric: '(type:cpu)', + nodeType: 'host', + region: '', + sort: { by: 'name', direction: 'desc' as const }, + timelineOpen: false, + view: 'map' as const, + }; + + const expected = Object.keys(params).reduce((acc: Record, key) => { + acc[key] = + key === 'metric' || key === 'customOptions' || key === 'customMetrics' + ? params[key] + : rison.encodeUnknown(params[key as keyof typeof params]); + return acc; + }, {}); + + const queryStringParams = querystring.stringify(expected); + + it('should create a link to Inventory with no state', async () => { + const { inventoryLocator } = await setupInventoryLocator(); + const { app, path, state } = await inventoryLocator.getLocation(params); + + expect(app).toBe('metrics'); + expect(path).toBe(`/inventory?${queryStringParams}`); + expect(state).toBeDefined(); + expect(Object.keys(state)).toHaveLength(0); + }); + + it('should return correct structured url', async () => { + const { inventoryLocator } = await setupInventoryLocator(); + const { app, path, state } = await inventoryLocator.getLocation(params); + + expect(app).toBe('metrics'); + expect(path).toBe(`/inventory?${queryStringParams}`); + expect(state).toBeDefined(); + expect(Object.keys(state)).toHaveLength(0); + }); + }); }); diff --git a/x-pack/plugins/observability_solution/observability_shared/public/plugin.ts b/x-pack/plugins/observability_solution/observability_shared/public/plugin.ts index 625bdbaaeeb3a..97808e516f320 100644 --- a/x-pack/plugins/observability_solution/observability_shared/public/plugin.ts +++ b/x-pack/plugins/observability_solution/observability_shared/public/plugin.ts @@ -27,6 +27,10 @@ import { AssetDetailsLocatorDefinition, } from './locators/infra/asset_details_locator'; import { type HostsLocator, HostsLocatorDefinition } from './locators/infra/hosts_locator'; +import { + type InventoryLocator, + InventoryLocatorDefinition, +} from './locators/infra/inventory_locator'; import { type FlamegraphLocator, FlamegraphLocatorDefinition, @@ -69,6 +73,7 @@ interface ObservabilitySharedLocators { assetDetailsLocator: AssetDetailsLocator; assetDetailsFlyoutLocator: AssetDetailsFlyoutLocator; hostsLocator: HostsLocator; + inventoryLocator: InventoryLocator; }; profiling: { flamegraphLocator: FlamegraphLocator; @@ -137,6 +142,7 @@ export class ObservabilitySharedPlugin implements Plugin { new AssetDetailsFlyoutLocatorDefinition() ), hostsLocator: urlService.locators.create(new HostsLocatorDefinition()), + inventoryLocator: urlService.locators.create(new InventoryLocatorDefinition()), }, profiling: { flamegraphLocator: urlService.locators.create(new FlamegraphLocatorDefinition()), diff --git a/x-pack/plugins/observability_solution/slo/e2e/journeys/index.ts b/x-pack/plugins/observability_solution/slo/e2e/journeys/index.ts new file mode 100644 index 0000000000000..c8a63354ad35d --- /dev/null +++ b/x-pack/plugins/observability_solution/slo/e2e/journeys/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './slos_overview.journey'; diff --git a/x-pack/plugins/observability_solution/slo/e2e/journeys/slos_overview.journey.ts b/x-pack/plugins/observability_solution/slo/e2e/journeys/slos_overview.journey.ts new file mode 100644 index 0000000000000..da83a88ac02d2 --- /dev/null +++ b/x-pack/plugins/observability_solution/slo/e2e/journeys/slos_overview.journey.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { journey, step, before, expect } from '@elastic/synthetics'; +import { RetryService } from '@kbn/ftr-common-functional-services'; +import { SLoDataService } from '../services/slo_data_service'; +import { sloAppPageProvider } from '../page_objects/slo_app'; + +journey(`SLOsOverview`, async ({ page, params }) => { + const sloApp = sloAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const dataService = new SLoDataService({ + kibanaUrl: params.kibanaUrl, + elasticsearchUrl: params.elasticsearchUrl, + getService: params.getService, + }); + + const retry: RetryService = params.getService('retry'); + + before(async () => { + await dataService.generateSloData(); + await dataService.addSLO(); + }); + + step('Go to slos overview', async () => { + await sloApp.navigateToOverview(true); + }); + + step('validate data retention tab', async () => { + await retry.tryWithRetries( + 'check if slos are displayed', + async () => { + await page.waitForSelector('text="Test Stack SLO"'); + const cards = await page.locator('text="Test Stack SLO"').all(); + expect(cards.length > 5).toBeTruthy(); + }, + { + retryCount: 50, + retryDelay: 20000, + timeout: 60 * 20000, + }, + async () => { + await page.getByTestId('querySubmitButton').click(); + } + ); + }); +}); diff --git a/x-pack/plugins/observability_solution/slo/e2e/page_objects/slo_app.tsx b/x-pack/plugins/observability_solution/slo/e2e/page_objects/slo_app.tsx new file mode 100644 index 0000000000000..09d3470bea775 --- /dev/null +++ b/x-pack/plugins/observability_solution/slo/e2e/page_objects/slo_app.tsx @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { Page } from '@elastic/synthetics'; +import { loginPageProvider } from '@kbn/synthetics-plugin/e2e/page_objects/login'; +import { utilsPageProvider } from '@kbn/synthetics-plugin/e2e/page_objects/utils'; +import { recordVideo } from '@kbn/synthetics-plugin/e2e/helpers/record_video'; + +export function sloAppPageProvider({ page, kibanaUrl }: { page: Page; kibanaUrl: string }) { + page.setDefaultTimeout(60 * 1000); + recordVideo(page); + + return { + ...loginPageProvider({ + page, + username: 'elastic', + password: 'changeme', + }), + ...utilsPageProvider({ page }), + + async navigateToOverview(doLogin = false) { + await page.goto(`${kibanaUrl}/app/slo`, { + waitUntil: 'networkidle', + }); + if (doLogin) { + await this.loginToKibana(); + } + }, + }; +} diff --git a/x-pack/plugins/observability_solution/slo/e2e/services/slo_data_service.ts b/x-pack/plugins/observability_solution/slo/e2e/services/slo_data_service.ts new file mode 100644 index 0000000000000..a6f30e24e1771 --- /dev/null +++ b/x-pack/plugins/observability_solution/slo/e2e/services/slo_data_service.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { KbnClient } from '@kbn/test'; +import { cli, DEFAULTS } from '@kbn/data-forge'; + +export class SLoDataService { + kibanaUrl: string; + elasticsearchUrl: string; + params: Record; + requester: KbnClient['requester']; + + constructor(params: Record) { + this.kibanaUrl = params.kibanaUrl; + this.elasticsearchUrl = params.elasticsearchUrl; + this.requester = params.getService('kibanaServer').requester; + this.params = params; + } + + async generateSloData({ + lookback = 'now-1d', + eventsPerCycle = 50, + }: { + lookback?: string; + eventsPerCycle?: number; + } = {}) { + await cli({ + kibanaUrl: this.kibanaUrl, + elasticsearchHost: this.elasticsearchUrl, + lookback: DEFAULTS.LOOKBACK, + eventsPerCycle: DEFAULTS.EVENTS_PER_CYCLE, + payloadSize: DEFAULTS.PAYLOAD_SIZE, + concurrency: DEFAULTS.CONCURRENCY, + indexInterval: 10_000, + dataset: 'fake_stack', + scenario: DEFAULTS.SCENARIO, + elasticsearchUsername: DEFAULTS.ELASTICSEARCH_USERNAME, + elasticsearchPassword: DEFAULTS.ELASTICSEARCH_PASSWORD, + kibanaUsername: DEFAULTS.KIBANA_USERNAME, + kibanaPassword: DEFAULTS.KIBANA_PASSWORD, + installKibanaAssets: true, + eventTemplate: DEFAULTS.EVENT_TEMPLATE, + reduceWeekendTrafficBy: DEFAULTS.REDUCE_WEEKEND_TRAFFIC_BY, + ephemeralProjectIds: DEFAULTS.EPHEMERAL_PROJECT_IDS, + alignEventsToInterval: DEFAULTS.ALIGN_EVENTS_TO_INTERVAL, + scheduleEnd: 'now+10m', + }).then((res) => { + // eslint-disable-next-line no-console + console.log(res); + }); + } + + async addSLO() { + const example = { + name: 'Test Stack SLO', + description: '', + indicator: { + type: 'sli.kql.custom', + params: { + index: 'kbn-data-forge-fake_stack.admin-console-*', + filter: '', + good: 'log.level : "INFO" ', + total: '', + timestampField: '@timestamp', + }, + }, + budgetingMethod: 'occurrences', + timeWindow: { + duration: '30d', + type: 'rolling', + }, + objective: { + target: 0.99, + }, + tags: [], + groupBy: ['user.id'], + }; + try { + const { data } = await this.requester.request({ + description: 'get monitor by id', + path: '/api/observability/slos', + body: example, + method: 'POST', + }); + return data; + } catch (e) { + console.error(e); + } + } +} diff --git a/x-pack/plugins/observability_solution/slo/e2e/synthetics_run.ts b/x-pack/plugins/observability_solution/slo/e2e/synthetics_run.ts new file mode 100644 index 0000000000000..d8a4946cfc447 --- /dev/null +++ b/x-pack/plugins/observability_solution/slo/e2e/synthetics_run.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { FtrConfigProviderContext } from '@kbn/test'; +import { SyntheticsRunner, argv } from '@kbn/synthetics-plugin/e2e'; + +const { headless, grep, bail: pauseOnError } = argv; + +async function runE2ETests({ readConfigFile }: FtrConfigProviderContext) { + const kibanaConfig = await readConfigFile(require.resolve('@kbn/synthetics-plugin/e2e/config')); + return { + ...kibanaConfig.getAll(), + testRunner: async ({ getService }: any) => { + const syntheticsRunner = new SyntheticsRunner(getService, { + headless, + match: grep, + pauseOnError, + }); + + await syntheticsRunner.setup(); + + await syntheticsRunner.loadTestFiles(async () => { + require('./journeys'); + }); + + await syntheticsRunner.run(); + }, + }; +} + +// eslint-disable-next-line import/no-default-export +export default runE2ETests; diff --git a/x-pack/plugins/observability_solution/slo/e2e/tsconfig.json b/x-pack/plugins/observability_solution/slo/e2e/tsconfig.json new file mode 100644 index 0000000000000..46ac09ac5a0d5 --- /dev/null +++ b/x-pack/plugins/observability_solution/slo/e2e/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../../../../tsconfig.base.json", + "exclude": ["tmp", "target/**/*"], + "include": ["**/*"], + "compilerOptions": { + "outDir": "target/types", + "types": [ "node"], + "isolatedModules": false, + }, + "kbn_references": [ + { "path": "../../../../test/tsconfig.json" }, + { "path": "../../../../../test/tsconfig.json" }, + "@kbn/test", + "@kbn/ftr-common-functional-services", + "@kbn/synthetics-plugin/e2e", + "@kbn/data-forge", + ] +} diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slos/components/card_view/slo_card_item.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slos/components/card_view/slo_card_item.tsx index deb2a1b880ad7..ebdc068435a76 100644 --- a/x-pack/plugins/observability_solution/slo/public/pages/slos/components/card_view/slo_card_item.tsx +++ b/x-pack/plugins/observability_solution/slo/public/pages/slos/components/card_view/slo_card_item.tsx @@ -108,6 +108,7 @@ export function SloCardItem({ slo, rules, activeAlerts, historicalSummary, refet return ( <> } onMouseOver={() => { if (!isMouseOver) { diff --git a/x-pack/plugins/observability_solution/slo/scripts/e2e.js b/x-pack/plugins/observability_solution/slo/scripts/e2e.js new file mode 100644 index 0000000000000..3fe4979236fb1 --- /dev/null +++ b/x-pack/plugins/observability_solution/slo/scripts/e2e.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* eslint-disable no-console */ +const { executeSyntheticsRunner } = require('@kbn/synthetics-plugin/scripts/base_e2e'); +const path = require('path'); + +const e2eDir = path.join(__dirname, '../e2e'); +executeSyntheticsRunner(e2eDir); diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/helpers/synthetics_runner.ts b/x-pack/plugins/observability_solution/synthetics/e2e/helpers/synthetics_runner.ts index 97e0fe72d81d6..11c71943c4710 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/helpers/synthetics_runner.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/helpers/synthetics_runner.ts @@ -112,7 +112,11 @@ export class SyntheticsRunner { let results: PromiseType> = {}; for (let i = 0; i < noOfRuns; i++) { results = await syntheticsRun({ - params: { kibanaUrl: this.kibanaUrl, getService: this.getService }, + params: { + kibanaUrl: this.kibanaUrl, + getService: this.getService, + elasticsearchUrl: this.elasticsearchUrl, + }, playwrightOptions: { headless: headless ?? !CI, testIdAttribute: 'data-test-subj', diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/index.ts b/x-pack/plugins/observability_solution/synthetics/e2e/index.ts new file mode 100644 index 0000000000000..f0e9b5e8b760c --- /dev/null +++ b/x-pack/plugins/observability_solution/synthetics/e2e/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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { SyntheticsRunner } from './helpers/synthetics_runner'; +export { argv } from './helpers/parse_args_params'; +export { loginPageProvider } from './page_objects/login'; +export { utilsPageProvider } from './page_objects/utils'; diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/add_monitor.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/add_monitor.journey.ts index e18b5cac11e2f..6e317150907ca 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/add_monitor.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/add_monitor.journey.ts @@ -6,7 +6,6 @@ */ import { v4 as uuidv4 } from 'uuid'; import { journey, step, expect, Page } from '@elastic/synthetics'; -import { recordVideo } from '../../helpers/record_video'; import { FormMonitorType } from '../../../common/runtime_types'; import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; @@ -147,10 +146,11 @@ const createMonitorJourney = ({ journey( `SyntheticsAddMonitor - ${monitorName}`, async ({ page, params }: { page: Page; params: any }) => { - page.setDefaultTimeout(60 * 1000); - recordVideo(page); - - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ + page, + kibanaUrl: params.kibanaUrl, + params, + }); step('Go to monitor management', async () => { await syntheticsApp.navigateToMonitorManagement(true); diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/alert_rules/default_status_alert.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/alert_rules/default_status_alert.journey.ts index 35b2bbbbef5f6..70327eb614ba7 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/alert_rules/default_status_alert.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/alert_rules/default_status_alert.journey.ts @@ -10,15 +10,11 @@ import { byTestId } from '@kbn/ux-plugin/e2e/journeys/utils'; import { RetryService } from '@kbn/ftr-common-functional-services'; import { v4 as uuidv4 } from 'uuid'; import { getReasonMessage } from '../../../../server/alert_rules/status_rule/message_utils'; -import { recordVideo } from '../../../helpers/record_video'; import { syntheticsAppPageProvider } from '../../page_objects/synthetics_app'; import { SyntheticsServices } from '../services/synthetics_services'; journey(`DefaultStatusAlert`, async ({ page, params }) => { - recordVideo(page); - - page.setDefaultTimeout(60 * 1000); - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); const services = new SyntheticsServices(params); diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/alerting_default.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/alerting_default.journey.ts index 96e91f81ff9fa..540a8e584cc5e 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/alerting_default.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/alerting_default.journey.ts @@ -6,17 +6,12 @@ */ import { journey, step, expect, before, after } from '@elastic/synthetics'; -import { recordVideo } from '../../helpers/record_video'; import { byTestId } from '../../helpers/utils'; import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; import { cleanSettings } from './services/settings'; journey('AlertingDefaults', async ({ page, params }) => { - recordVideo(page); - - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); - - page.setDefaultTimeout(60 * 1000); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); before(async () => { await cleanSettings(params); diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/data_retention.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/data_retention.journey.ts index 1e0c00f42a92f..7f7a395b09b36 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/data_retention.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/data_retention.journey.ts @@ -13,9 +13,7 @@ import { byTestId, assertText } from '../../helpers/utils'; let page1: Page; journey(`DataRetentionPage`, async ({ page, params }) => { - page.setDefaultTimeout(60 * 1000); - recordVideo(page); - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); const getService = params.getService; const retry: RetryService = getService('retry'); diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/detail_flyout.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/detail_flyout.ts index 4204e68099042..037ad7bbb651a 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/detail_flyout.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/detail_flyout.ts @@ -6,13 +6,10 @@ */ import { expect, journey, step } from '@elastic/synthetics'; -import { recordVideo } from '../../helpers/record_video'; import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; journey('TestMonitorDetailFlyout', async ({ page, params }) => { - recordVideo(page); - - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); const monitorName = 'test-flyout-http-monitor'; step('Go to monitor-management', async () => { diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/getting_started.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/getting_started.journey.ts index abb66db15d601..78261c08e4415 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/getting_started.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/getting_started.journey.ts @@ -6,14 +6,11 @@ */ import { journey, step, expect, before, Page } from '@elastic/synthetics'; -import { recordVideo } from '../../helpers/record_video'; import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; import { cleanTestMonitors } from './services/add_monitor'; journey(`Getting Started Page`, async ({ page, params }: { page: Page; params: any }) => { - recordVideo(page); - - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); const createBasicMonitor = async () => { await syntheticsApp.fillFirstMonitorDetails({ diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/global_parameters.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/global_parameters.journey.ts index d5581e51b1833..3ff9423180686 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/global_parameters.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/global_parameters.journey.ts @@ -7,14 +7,11 @@ import { journey, step, before, after, expect } from '@elastic/synthetics'; import { byTestId } from '../../helpers/utils'; -import { recordVideo } from '../../helpers/record_video'; import { cleanTestParams } from './services/add_monitor'; import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; journey(`GlobalParameters`, async ({ page, params }) => { - recordVideo(page); - - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); before(async () => { await cleanTestParams(params); diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/management_list.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/management_list.journey.ts index b3a4db77680e0..c81fe084194d5 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/management_list.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/management_list.journey.ts @@ -7,7 +7,6 @@ import { journey, step, expect, before, after } from '@elastic/synthetics'; import { byTestId } from '../../helpers/utils'; -import { recordVideo } from '../../helpers/record_video'; import { addTestMonitor, cleanTestMonitors, @@ -16,9 +15,7 @@ import { import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; journey(`MonitorManagementList`, async ({ page, params }) => { - recordVideo(page); - - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); const testMonitor1 = 'Test monitor 1'; const testMonitor2 = 'Test monitor 2'; const testMonitor3 = 'Test monitor 3'; diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/monitor_details_page/monitor_summary.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/monitor_details_page/monitor_summary.journey.ts index 34b4e0ca985f7..46fa7837ded99 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/monitor_details_page/monitor_summary.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/monitor_details_page/monitor_summary.journey.ts @@ -9,15 +9,11 @@ import { journey, step, before, after, expect } from '@elastic/synthetics'; import { byTestId } from '@kbn/ux-plugin/e2e/journeys/utils'; import { RetryService } from '@kbn/ftr-common-functional-services'; import moment from 'moment'; -import { recordVideo } from '../../../helpers/record_video'; import { syntheticsAppPageProvider } from '../../page_objects/synthetics_app'; import { SyntheticsServices } from '../services/synthetics_services'; journey(`MonitorSummaryTab`, async ({ page, params }) => { - recordVideo(page); - - page.setDefaultTimeout(60 * 1000); - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); const services = new SyntheticsServices(params); diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/monitor_form_validation.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/monitor_form_validation.journey.ts index c34ea3fa7c8cb..7fdce8d921055 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/monitor_form_validation.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/monitor_form_validation.journey.ts @@ -6,7 +6,6 @@ */ import { expect, journey, Page, step } from '@elastic/synthetics'; import { FormMonitorType } from '../../../common/runtime_types'; -import { recordVideo } from '../../helpers/record_video'; import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; import { isEuiFormFieldInValid, @@ -362,10 +361,7 @@ const exitingMonitorConfig = { journey( `SyntheticsAddMonitor - Validation Test`, async ({ page, params }: { page: Page; params: any }) => { - page.setDefaultTimeout(60 * 1000); - recordVideo(page); - - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); step('Go to monitor management', async () => { await syntheticsApp.navigateToMonitorManagement(true); diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/monitor_selector.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/monitor_selector.journey.ts index 35e4a8996d5b3..73b7e6e53ca1d 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/monitor_selector.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/monitor_selector.journey.ts @@ -7,7 +7,6 @@ import { journey, step, expect, before, after } from '@elastic/synthetics'; import { byTestId } from '../../helpers/utils'; -import { recordVideo } from '../../helpers/record_video'; import { addTestMonitor, cleanTestMonitors, @@ -16,9 +15,7 @@ import { import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; journey(`MonitorSelector`, async ({ page, params }) => { - recordVideo(page); - - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); const testMonitor1 = 'Test monitor 1'; const testMonitor2 = 'Test monitor 2'; const testMonitor3 = 'Test monitor 3'; diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/overview_scrolling.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/overview_scrolling.journey.ts index 7bec9a7a4b389..30b8a4f456a0d 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/overview_scrolling.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/overview_scrolling.journey.ts @@ -7,7 +7,6 @@ import { before, after, expect, journey, step } from '@elastic/synthetics'; import { RetryService } from '@kbn/ftr-common-functional-services'; -import { recordVideo } from '../../helpers/record_video'; import { addTestMonitor, cleanTestMonitors, @@ -16,9 +15,7 @@ import { import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; journey('OverviewScrolling', async ({ page, params }) => { - recordVideo(page); - - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); const retry: RetryService = params.getService('retry'); const listOfRequests: string[] = []; diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/overview_search.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/overview_search.journey.ts index a1729fca0faa3..790cb06f5f5c2 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/overview_search.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/overview_search.journey.ts @@ -7,7 +7,6 @@ import { before, expect, journey, step } from '@elastic/synthetics'; import { RetryService } from '@kbn/ftr-common-functional-services'; -import { recordVideo } from '../../helpers/record_video'; import { addTestMonitor, cleanTestMonitors, @@ -16,11 +15,9 @@ import { import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; journey('Overview Search', async ({ page, params }) => { - recordVideo(page); - const retry: RetryService = params.getService('retry'); - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); const elasticJourney = 'Elastic journey'; const cnnJourney = 'CNN journey'; const googleJourney = 'Google journey'; diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/overview_sorting.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/overview_sorting.journey.ts index 018df450628ef..cd65a67f1e22a 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/overview_sorting.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/overview_sorting.journey.ts @@ -6,7 +6,6 @@ */ import { before, expect, journey, step } from '@elastic/synthetics'; -import { recordVideo } from '../../helpers/record_video'; import { addTestMonitor, cleanTestMonitors, @@ -15,9 +14,7 @@ import { import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; journey('OverviewSorting', async ({ page, params }) => { - recordVideo(page); - - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); const testMonitor1 = 'acb'; // second alpha, first created const testMonitor2 = 'aCd'; // third alpha, second created const testMonitor3 = 'Abc'; // first alpha, last created diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/private_locations.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/private_locations.journey.ts index b9a9b23d16ca7..9e6bb8352c35f 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/private_locations.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/private_locations.journey.ts @@ -8,7 +8,6 @@ import { journey, step, before, after, expect } from '@elastic/synthetics'; import { waitForLoadingToFinish } from '@kbn/ux-plugin/e2e/journeys/utils'; import { byTestId } from '../../helpers/utils'; -import { recordVideo } from '../../helpers/record_video'; import { addTestMonitor, cleanPrivateLocations, @@ -18,9 +17,7 @@ import { import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; journey(`PrivateLocationsSettings`, async ({ page, params }) => { - recordVideo(page); - - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); page.setDefaultTimeout(2 * 30000); diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/project_monitor_read_only.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/project_monitor_read_only.journey.ts index 2bfc97bb9f5f1..4c9d73f3be815 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/project_monitor_read_only.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/project_monitor_read_only.journey.ts @@ -7,18 +7,15 @@ import { after, before, expect, journey, step } from '@elastic/synthetics'; import { SyntheticsServices } from './services/synthetics_services'; -import { recordVideo } from '../../helpers/record_video'; import { cleanTestMonitors, enableMonitorManagedViaApi } from './services/add_monitor'; import { addTestMonitorProject } from './services/add_monitor_project'; import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; import { SyntheticsMonitor } from '../../../common/runtime_types'; journey('ProjectMonitorReadOnly', async ({ page, params }) => { - recordVideo(page); - const services = new SyntheticsServices(params); - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); let originalMonitorConfiguration: SyntheticsMonitor | null; let monitorId: string; diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/step_details.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/step_details.journey.ts index 5d4c677b21718..07dab69c33ed8 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/step_details.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/step_details.journey.ts @@ -6,15 +6,11 @@ */ import { journey, step, before, after } from '@elastic/synthetics'; -import { recordVideo } from '../../helpers/record_video'; import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; import { SyntheticsServices } from './services/synthetics_services'; journey(`StepDetailsPage`, async ({ page, params }) => { - recordVideo(page); - - page.setDefaultTimeout(60 * 1000); - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); const services = new SyntheticsServices(params); diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/test_now_mode.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/test_now_mode.journey.ts index ac931bbd1e725..2d8b4de8e5b40 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/test_now_mode.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/test_now_mode.journey.ts @@ -7,15 +7,12 @@ import { journey, step, before, after, expect } from '@elastic/synthetics'; import { RetryService } from '@kbn/ftr-common-functional-services'; -import { recordVideo } from '../../helpers/record_video'; import { byTestId } from '../../helpers/utils'; import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; import { SyntheticsServices } from './services/synthetics_services'; journey(`TestNowMode`, async ({ page, params }) => { - page.setDefaultTimeout(60 * 1000); - recordVideo(page); - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); const services = new SyntheticsServices(params); diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/test_run_details.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/test_run_details.journey.ts index 5c1af51caa18d..8bf7fd0f690d1 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/test_run_details.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/test_run_details.journey.ts @@ -6,16 +6,12 @@ */ import { journey, step, before, after, expect } from '@elastic/synthetics'; -import { recordVideo } from '../../helpers/record_video'; import { byTestId } from '../../helpers/utils'; import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; import { SyntheticsServices } from './services/synthetics_services'; journey(`TestRunDetailsPage`, async ({ page, params }) => { - recordVideo(page); - - page.setDefaultTimeout(60 * 1000); - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); const services = new SyntheticsServices(params); diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/page_objects/synthetics_app.tsx b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/page_objects/synthetics_app.tsx index 4e16479d10434..49aefae295b39 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/page_objects/synthetics_app.tsx +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/page_objects/synthetics_app.tsx @@ -5,6 +5,8 @@ * 2.0. */ import { expect, Page } from '@elastic/synthetics'; +import { RetryService } from '@kbn/ftr-common-functional-services'; +import { recordVideo } from '../../helpers/record_video'; import { FormMonitorType } from '../../../common/runtime_types/monitor_management'; import { loginPageProvider } from '../../page_objects/login'; import { utilsPageProvider } from '../../page_objects/utils'; @@ -13,7 +15,15 @@ const SIXTY_SEC_TIMEOUT = { timeout: 60 * 1000, }; -export function syntheticsAppPageProvider({ page, kibanaUrl }: { page: Page; kibanaUrl: string }) { +export function syntheticsAppPageProvider({ + page, + kibanaUrl, + params, +}: { + page: Page; + kibanaUrl: string; + params: Record; +}) { const remoteKibanaUrl = process.env.SYNTHETICS_REMOTE_KIBANA_URL; const remoteUsername = process.env.SYNTHETICS_REMOTE_KIBANA_USERNAME; const remotePassword = process.env.SYNTHETICS_REMOTE_KIBANA_PASSWORD; @@ -23,6 +33,10 @@ export function syntheticsAppPageProvider({ page, kibanaUrl }: { page: Page; kib const settingsPage = `${basePath}/app/synthetics/settings`; const addMonitor = `${basePath}/app/synthetics/add-monitor`; const overview = `${basePath}/app/synthetics`; + const retry: RetryService = params?.getService('retry'); + + recordVideo(page); + page.setDefaultTimeout(60 * 1000); return { ...loginPageProvider({ @@ -162,8 +176,10 @@ export function syntheticsAppPageProvider({ page, kibanaUrl }: { page: Page; kib async selectLocationsAddEdit({ locations }: { locations: string[] }) { for (let i = 0; i < locations.length; i++) { - await page.click(this.byTestId('syntheticsMonitorConfigLocations')); - await page.click(`text=${locations[i]}`); + await retry.try(async () => { + await page.click(this.byTestId('syntheticsMonitorConfigLocations')); + await page.click(`text=${locations[i]}`); + }); } }, @@ -278,9 +294,6 @@ export function syntheticsAppPageProvider({ page, kibanaUrl }: { page: Page; kib name, inlineScript, recorderScript, - params, - username, - password, apmServiceName, locations, }: { diff --git a/x-pack/plugins/observability_solution/synthetics/public/plugin.ts b/x-pack/plugins/observability_solution/synthetics/public/plugin.ts index 1d7044a79c6bf..7c343ef795776 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/plugin.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/plugin.ts @@ -238,7 +238,6 @@ function registerSyntheticsRoutesWithNavigation( path: OVERVIEW_ROUTE, matchFullPath: true, ignoreTrailingSlash: true, - isNewFeature: true, }, { label: i18n.translate('xpack.synthetics.certificatesPage.heading', { diff --git a/x-pack/plugins/osquery/cypress/e2e/all/saved_queries.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/saved_queries.cy.ts index 6638567a92625..27fe443eed062 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/saved_queries.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/saved_queries.cy.ts @@ -76,7 +76,8 @@ describe('ALL - Saved queries', { tags: ['@ess', '@serverless'] }, () => { }); }); - describe('prebuilt', () => { + // FAILING ES SERVERLESS PROMOTION: https://github.com/elastic/kibana/issues/169787 + describe.skip('prebuilt', () => { let packName: string; let packId: string; let savedQueryId: string; diff --git a/x-pack/plugins/rule_registry/server/utils/get_alerts_for_notification.test.ts b/x-pack/plugins/rule_registry/server/utils/get_alerts_for_notification.test.ts index a6f428d598262..f6ce39f34d0a1 100644 --- a/x-pack/plugins/rule_registry/server/utils/get_alerts_for_notification.test.ts +++ b/x-pack/plugins/rule_registry/server/utils/get_alerts_for_notification.test.ts @@ -245,6 +245,13 @@ describe('getAlertsForNotification', () => { ).toMatchInlineSnapshot(`Array []`); }); + test('should not return recovered alerts if the activeCount is less than the rule alertDelay', () => { + const trackedEvents = cloneDeep([alert1]); + expect( + getAlertsForNotification(DEFAULT_FLAPPING_SETTINGS, 5, trackedEvents, [], newEventParams) + ).toMatchInlineSnapshot(`Array []`); + }); + test('should update active alert to look like a new alert if the activeCount is equal to the rule alertDelay', () => { const trackedEvents = cloneDeep([alert5]); expect( diff --git a/x-pack/plugins/rule_registry/server/utils/get_alerts_for_notification.ts b/x-pack/plugins/rule_registry/server/utils/get_alerts_for_notification.ts index 15dcedeaf88ca..73cbac1b2c90a 100644 --- a/x-pack/plugins/rule_registry/server/utils/get_alerts_for_notification.ts +++ b/x-pack/plugins/rule_registry/server/utils/get_alerts_for_notification.ts @@ -56,6 +56,10 @@ export function getAlertsForNotification( } } } else if (trackedEvent.event[ALERT_STATUS] === ALERT_STATUS_RECOVERED) { + // if alert has not reached the alertDelay threshold don't recover the alert + if (trackedEvent.activeCount < alertDelay) { + continue; + } trackedEvent.activeCount = 0; if (flappingSettings.enabled) { if (trackedEvent.flapping) { diff --git a/x-pack/plugins/search_playground/kibana.jsonc b/x-pack/plugins/search_playground/kibana.jsonc index 221d5bf9e1873..28a775eca341e 100644 --- a/x-pack/plugins/search_playground/kibana.jsonc +++ b/x-pack/plugins/search_playground/kibana.jsonc @@ -21,6 +21,7 @@ ], "optionalPlugins": [ "cloud", + "console", "usageCollection", ], "requiredBundles": [ diff --git a/x-pack/plugins/search_playground/public/chat_playground_overview.tsx b/x-pack/plugins/search_playground/public/chat_playground_overview.tsx index b750a9828021d..c6443c912bda0 100644 --- a/x-pack/plugins/search_playground/public/chat_playground_overview.tsx +++ b/x-pack/plugins/search_playground/public/chat_playground_overview.tsx @@ -6,16 +6,26 @@ */ import { i18n } from '@kbn/i18n'; -import React from 'react'; -import { EuiBetaBadge, EuiFlexGroup, EuiFlexItem, EuiPageTemplate } from '@elastic/eui'; +import React, { useMemo } from 'react'; +import { EuiBetaBadge, EuiFlexGroup, EuiFlexItem, EuiPageTemplate, EuiTitle } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { PlaygroundProvider } from './providers/playground_provider'; import { App } from './components/app'; import { PlaygroundToolbar } from './embeddable'; import { PlaygroundHeaderDocs } from './components/playground_header_docs'; +import { useKibana } from './hooks/use_kibana'; export const ChatPlaygroundOverview: React.FC = () => { + const { + services: { console: consolePlugin }, + } = useKibana(); + + const embeddableConsole = useMemo( + () => (consolePlugin?.EmbeddableConsole ? : null), + [consolePlugin] + ); + return ( { grow={false} > .euiFlexGroup': { flexWrap: 'wrap' } }} pageTitle={ - - - + +

+ +

+
{ rightSideItems={[, ]} /> + {embeddableConsole}
); diff --git a/x-pack/plugins/search_playground/public/components/chat.tsx b/x-pack/plugins/search_playground/public/components/chat.tsx index 59e52778e42ca..6cf82dd1ed2e1 100644 --- a/x-pack/plugins/search_playground/public/components/chat.tsx +++ b/x-pack/plugins/search_playground/public/components/chat.tsx @@ -117,6 +117,7 @@ export const Chat = () => { component="form" css={{ display: 'flex', flexGrow: 1 }} onSubmit={handleSubmit(onSubmit)} + data-test-subj="chatPage" > { isLoading={isSubmitting} isDisabled={!isValid} iconType={TelegramIcon} + data-test-subj="sendQuestionButton" /> ) } diff --git a/x-pack/plugins/search_playground/public/components/edit_context/edit_context_action.tsx b/x-pack/plugins/search_playground/public/components/edit_context/edit_context_action.tsx index 45e956472bf9a..6e9a638248660 100644 --- a/x-pack/plugins/search_playground/public/components/edit_context/edit_context_action.tsx +++ b/x-pack/plugins/search_playground/public/components/edit_context/edit_context_action.tsx @@ -22,7 +22,11 @@ export const EditContextAction: React.FC = () => { return ( <> {showFlyout && } - setShowFlyout(true)} disabled={selectedIndices?.length === 0}> + setShowFlyout(true)} + disabled={selectedIndices?.length === 0} + data-test-subj="editContextActionButton" + > = ({ onClose }) }, [usageTracker]); return ( - +

@@ -172,6 +172,7 @@ export const EditContextFlyout: React.FC = ({ onClose }) options={group.source_fields.map((field) => ({ value: field, inputDisplay: field, + 'data-test-subj': 'contextField', }))} valueOfSelected={tempSourceFields[index]?.[0]} onChange={(value) => updateSourceField(index, value)} diff --git a/x-pack/plugins/search_playground/public/components/message_list/user_message.tsx b/x-pack/plugins/search_playground/public/components/message_list/user_message.tsx index b2ad7d6e1b647..12c1d86283404 100644 --- a/x-pack/plugins/search_playground/public/components/message_list/user_message.tsx +++ b/x-pack/plugins/search_playground/public/components/message_list/user_message.tsx @@ -57,6 +57,7 @@ export const UserMessage: React.FC = ({ content, createdAt }) })} /> } + data-test-subj="userMessage" >

{content}

diff --git a/x-pack/plugins/search_playground/public/components/question_input.tsx b/x-pack/plugins/search_playground/public/components/question_input.tsx index 0077e2ac7bf73..cdc9d347e4ff8 100644 --- a/x-pack/plugins/search_playground/public/components/question_input.tsx +++ b/x-pack/plugins/search_playground/public/components/question_input.tsx @@ -66,6 +66,7 @@ export const QuestionInput: React.FC = ({ onChange={handleChange} disabled={isDisabled} resize="none" + data-test-subj="questionInput" />
= ({ disabled: selectedIndices.includes(index), }))} isClearable={false} + data-test-subj="selectIndicesComboBox" /> ); diff --git a/x-pack/plugins/search_playground/public/components/start_new_chat.tsx b/x-pack/plugins/search_playground/public/components/start_new_chat.tsx index 0db891438cb3d..46d3896e9e953 100644 --- a/x-pack/plugins/search_playground/public/components/start_new_chat.tsx +++ b/x-pack/plugins/search_playground/public/components/start_new_chat.tsx @@ -41,7 +41,7 @@ export const StartNewChat: React.FC = ({ onStartClick }) => { }, [usageTracker]); return ( - + = ({ onStartClick }) => { - + = ({ onStartClick }) => { !watch(ChatFormFields.elasticsearchQuery, '') } onClick={onStartClick} + data-test-subj="startChatButton" > { fill onClick={() => setShowFlyout(true)} disabled={!selectedIndices || selectedIndices?.length === 0} + data-test-subj="viewCodeActionButton" > = ({ onClose }) => { }, [usageTracker, selectedLanguage]); return ( - +

diff --git a/x-pack/plugins/search_playground/public/components/view_query/view_query_action.tsx b/x-pack/plugins/search_playground/public/components/view_query/view_query_action.tsx index 9b691283bab90..53d01dc81d614 100644 --- a/x-pack/plugins/search_playground/public/components/view_query/view_query_action.tsx +++ b/x-pack/plugins/search_playground/public/components/view_query/view_query_action.tsx @@ -20,7 +20,11 @@ export const ViewQueryAction: React.FC = () => { return ( <> {showFlyout && setShowFlyout(false)} />} - setShowFlyout(true)} disabled={selectedIndices?.length === 0}> + setShowFlyout(true)} + disabled={selectedIndices?.length === 0} + data-test-subj="viewQueryActionButton" + > = ({ onClose }) => }, [usageTracker]); return ( - +

@@ -216,6 +216,7 @@ export const ViewQueryFlyout: React.FC = ({ onClose }) => /> ), checked: checked ? 'on' : undefined, + 'data-test-subj': 'queryField', }; })} listProps={{ diff --git a/x-pack/plugins/search_playground/public/types.ts b/x-pack/plugins/search_playground/public/types.ts index 6f8552d753bbb..84edcf1b07e05 100644 --- a/x-pack/plugins/search_playground/public/types.ts +++ b/x-pack/plugins/search_playground/public/types.ts @@ -21,6 +21,7 @@ import { CloudSetup } from '@kbn/cloud-plugin/public'; import { TriggersAndActionsUIPublicPluginStart } from '@kbn/triggers-actions-ui-plugin/public'; import { AppMountParameters } from '@kbn/core/public'; import { UsageCollectionStart } from '@kbn/usage-collection-plugin/public'; +import type { ConsolePluginStart } from '@kbn/console-plugin/public'; import { ChatRequestData } from '../common/types'; import type { App } from './components/app'; import type { PlaygroundProvider as PlaygroundProviderComponent } from './providers/playground_provider'; @@ -44,6 +45,7 @@ export interface AppPluginStartDependencies { navigation: NavigationPublicPluginStart; triggersActionsUi: TriggersAndActionsUIPublicPluginStart; share: SharePluginStart; + console?: ConsolePluginStart; } export interface AppServicesContext { @@ -53,6 +55,7 @@ export interface AppServicesContext { cloud?: CloudSetup; triggersActionsUi: TriggersAndActionsUIPublicPluginStart; usageCollection?: UsageCollectionStart; + console?: ConsolePluginStart; } export enum ChatFormFields { diff --git a/x-pack/plugins/search_playground/server/lib/fetch_indices.test.ts b/x-pack/plugins/search_playground/server/lib/fetch_indices.test.ts index 8613609b38fc4..af9c6adfc8ae2 100644 --- a/x-pack/plugins/search_playground/server/lib/fetch_indices.test.ts +++ b/x-pack/plugins/search_playground/server/lib/fetch_indices.test.ts @@ -19,8 +19,8 @@ describe('fetch indices', () => { }, 'index-2': { aliases: { - 'search-alias-1': {}, - 'search-alias-2': {}, + 'search-alias-3': {}, + 'search-alias-4': {}, }, }, 'index-3': { @@ -48,7 +48,15 @@ describe('fetch indices', () => { ); expect(indexData).toEqual({ - indexNames: ['index-1', 'index-2', 'index-3'], + indexNames: [ + 'index-1', + 'index-2', + 'index-3', + 'search-alias-1', + 'search-alias-2', + 'search-alias-3', + 'search-alias-4', + ], }); }); }); diff --git a/x-pack/plugins/search_playground/server/lib/fetch_indices.ts b/x-pack/plugins/search_playground/server/lib/fetch_indices.ts index 953e7a980a6a9..51b72790028c2 100644 --- a/x-pack/plugins/search_playground/server/lib/fetch_indices.ts +++ b/x-pack/plugins/search_playground/server/lib/fetch_indices.ts @@ -42,9 +42,24 @@ export const fetchIndices = async ( !isHidden(allIndexMatches[indexName]) && !isClosed(allIndexMatches[indexName]) ); + + const allAliases = allIndexNames.reduce((acc, indexName) => { + const aliases = allIndexMatches[indexName].aliases; + if (aliases) { + Object.keys(aliases).forEach((alias) => { + if (!acc.includes(alias)) { + acc.push(alias); + } + }); + } + return acc; + }, []); + + const allOptions = [...allIndexNames, ...allAliases]; + const indexNames = searchQuery - ? allIndexNames.filter((indexName) => indexName.includes(searchQuery.toLowerCase())) - : allIndexNames; + ? allOptions.filter((indexName) => indexName.includes(searchQuery.toLowerCase())) + : allOptions; return { indexNames, diff --git a/x-pack/plugins/search_playground/server/lib/get_chat_params.test.ts b/x-pack/plugins/search_playground/server/lib/get_chat_params.test.ts index cb409461d0a48..624f6e0aa637f 100644 --- a/x-pack/plugins/search_playground/server/lib/get_chat_params.test.ts +++ b/x-pack/plugins/search_playground/server/lib/get_chat_params.test.ts @@ -84,7 +84,16 @@ describe('getChatParams', () => { context: true, type: 'anthropic', }); - expect(ActionsClientLlm).toHaveBeenCalledWith(expect.anything()); + expect(ActionsClientLlm).toHaveBeenCalledWith({ + temperature: 0, + llmType: 'bedrock', + traceId: 'test-uuid', + request: expect.anything(), + logger: expect.anything(), + model: 'custom-model', + connectorId: '2', + actions: expect.anything(), + }); expect(result.chatPrompt).toContain('How does it work?'); }); diff --git a/x-pack/plugins/search_playground/server/lib/get_chat_params.ts b/x-pack/plugins/search_playground/server/lib/get_chat_params.ts index 2dba42c668c77..8a5aeb68c7e76 100644 --- a/x-pack/plugins/search_playground/server/lib/get_chat_params.ts +++ b/x-pack/plugins/search_playground/server/lib/get_chat_params.ts @@ -16,6 +16,7 @@ import type { PluginStartContract as ActionsPluginStartContract } from '@kbn/act import type { KibanaRequest, Logger } from '@kbn/core/server'; import { BaseLanguageModel } from '@langchain/core/language_models/base'; import type { Connector } from '@kbn/actions-plugin/server/application/connector/types'; +import { getDefaultArguments } from '@kbn/elastic-assistant-common/impl/language_models/constants'; import { Prompt } from '../../common/prompt'; export const getChatParams = async ( @@ -52,6 +53,7 @@ export const getChatParams = async ( model, traceId: uuidv4(), signal: abortSignal, + temperature: getDefaultArguments().temperature, // prevents the agent from retrying on failure // failure could be due to bad connector, we should deliver that result to the client asap maxRetries: 0, @@ -63,6 +65,7 @@ export const getChatParams = async ( }); break; case BEDROCK_CONNECTOR_ID: + const llmType = 'bedrock'; chatModel = new ActionsClientLlm({ actions, logger, @@ -70,6 +73,8 @@ export const getChatParams = async ( connectorId, model, traceId: uuidv4(), + llmType, + temperature: getDefaultArguments(llmType).temperature, }); chatPrompt = Prompt(prompt, { citations, diff --git a/x-pack/plugins/search_playground/tsconfig.json b/x-pack/plugins/search_playground/tsconfig.json index 2fc516bc2ca46..96c2c1681008a 100644 --- a/x-pack/plugins/search_playground/tsconfig.json +++ b/x-pack/plugins/search_playground/tsconfig.json @@ -39,7 +39,8 @@ "@kbn/core-logging-server-mocks", "@kbn/analytics", "@kbn/usage-collection-plugin", - "@kbn/analytics-client" + "@kbn/analytics-client", + "@kbn/console-plugin" ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.gen.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.gen.ts index 6b6bc018c8e5c..ac6eb3dd18a7e 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.gen.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.gen.ts @@ -303,13 +303,40 @@ export const TimestampOverride = z.string(); export type TimestampOverrideFallbackDisabled = z.infer; export const TimestampOverrideFallbackDisabled = z.boolean(); +/** + * Describes an Elasticsearch field that is needed for the rule to function + */ export type RequiredField = z.infer; export const RequiredField = z.object({ + /** + * Name of an Elasticsearch field + */ name: NonEmptyString, + /** + * Type of the Elasticsearch field + */ type: NonEmptyString, + /** + * Whether the field is an ECS field + */ ecs: z.boolean(), }); +/** + * Input parameters to create a RequiredField. Does not include the `ecs` field, because `ecs` is calculated on the backend based on the field name and type. + */ +export type RequiredFieldInput = z.infer; +export const RequiredFieldInput = z.object({ + /** + * Name of an Elasticsearch field + */ + name: NonEmptyString, + /** + * Type of an Elasticsearch field + */ + type: NonEmptyString, +}); + export type RequiredFieldArray = z.infer; export const RequiredFieldArray = z.array(RequiredField); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.schema.yaml index fadb38ef1ce5f..cd5e238723f6a 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.schema.yaml @@ -315,18 +315,36 @@ components: RequiredField: type: object + description: Describes an Elasticsearch field that is needed for the rule to function properties: name: $ref: '../../../model/primitives.schema.yaml#/components/schemas/NonEmptyString' + description: Name of an Elasticsearch field type: $ref: '../../../model/primitives.schema.yaml#/components/schemas/NonEmptyString' + description: Type of the Elasticsearch field ecs: type: boolean + description: Whether the field is an ECS field required: - name - type - ecs + RequiredFieldInput: + type: object + description: Input parameters to create a RequiredField. Does not include the `ecs` field, because `ecs` is calculated on the backend based on the field name and type. + properties: + name: + $ref: '../../../model/primitives.schema.yaml#/components/schemas/NonEmptyString' + description: Name of an Elasticsearch field + type: + $ref: '../../../model/primitives.schema.yaml#/components/schemas/NonEmptyString' + description: Type of an Elasticsearch field + required: + - name + - type + RequiredFieldArray: type: array items: diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_request_schema.test.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_request_schema.test.ts index 0b89cbdc72889..b7435c7dd86e8 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_request_schema.test.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_request_schema.test.ts @@ -1267,6 +1267,7 @@ describe('rules schema', () => { // behaviour common for multiple rule types const cases = [ { ruleType: 'threat_match', ruleMock: getCreateThreatMatchRulesSchemaMock() }, + { ruleType: 'esql', ruleMock: getCreateEsqlRulesSchemaMock() }, { ruleType: 'query', ruleMock: getCreateRulesSchemaMock() }, { ruleType: 'saved_query', ruleMock: getCreateSavedQueryRulesSchemaMock() }, { ruleType: 'eql', ruleMock: getCreateEqlRuleSchemaMock() }, diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.gen.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.gen.ts index d05a272337534..278b4679cd93e 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.gen.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.gen.ts @@ -54,6 +54,7 @@ import { ThreatArray, SetupGuide, RelatedIntegrationArray, + RequiredFieldInput, RuleObjectId, RuleSignatureId, IsRuleImmutable, @@ -137,6 +138,7 @@ export const BaseDefaultableFields = z.object({ threat: ThreatArray.optional(), setup: SetupGuide.optional(), related_integrations: RelatedIntegrationArray.optional(), + required_fields: z.array(RequiredFieldInput).optional(), }); export type BaseCreateProps = z.infer; @@ -558,14 +560,19 @@ export const EsqlRuleRequiredFields = z.object({ query: RuleQuery, }); +export type EsqlRuleOptionalFields = z.infer; +export const EsqlRuleOptionalFields = z.object({ + alert_suppression: AlertSuppression.optional(), +}); + export type EsqlRulePatchFields = z.infer; -export const EsqlRulePatchFields = EsqlRuleRequiredFields.partial(); +export const EsqlRulePatchFields = EsqlRuleOptionalFields.merge(EsqlRuleRequiredFields.partial()); export type EsqlRuleResponseFields = z.infer; -export const EsqlRuleResponseFields = EsqlRuleRequiredFields; +export const EsqlRuleResponseFields = EsqlRuleOptionalFields.merge(EsqlRuleRequiredFields); export type EsqlRuleCreateFields = z.infer; -export const EsqlRuleCreateFields = EsqlRuleRequiredFields; +export const EsqlRuleCreateFields = EsqlRuleOptionalFields.merge(EsqlRuleRequiredFields); export type EsqlRule = z.infer; export const EsqlRule = SharedResponseProps.merge(EsqlRuleResponseFields); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.schema.yaml index f8998624f99b1..de424af505c1f 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.schema.yaml @@ -117,12 +117,15 @@ components: author: $ref: './common_attributes.schema.yaml#/components/schemas/RuleAuthorArray' + # False positive examples false_positives: $ref: './common_attributes.schema.yaml#/components/schemas/RuleFalsePositiveArray' + # Reference URLs references: $ref: './common_attributes.schema.yaml#/components/schemas/RuleReferenceArray' + # Max alerts per run max_signals: $ref: './common_attributes.schema.yaml#/components/schemas/MaxSignals' threat: @@ -130,9 +133,16 @@ components: setup: $ref: './common_attributes.schema.yaml#/components/schemas/SetupGuide' + # Related integrations related_integrations: $ref: './common_attributes.schema.yaml#/components/schemas/RelatedIntegrationArray' + # Required fields + required_fields: + type: array + items: + $ref: './common_attributes.schema.yaml#/components/schemas/RequiredFieldInput' + BaseCreateProps: x-inline: true allOf: @@ -816,17 +826,26 @@ components: - language - query + EsqlRuleOptionalFields: + type: object + properties: + alert_suppression: + $ref: './common_attributes.schema.yaml#/components/schemas/AlertSuppression' + EsqlRulePatchFields: allOf: + - $ref: '#/components/schemas/EsqlRuleOptionalFields' - $ref: '#/components/schemas/EsqlRuleRequiredFields' x-modify: partial EsqlRuleResponseFields: allOf: + - $ref: '#/components/schemas/EsqlRuleOptionalFields' - $ref: '#/components/schemas/EsqlRuleRequiredFields' EsqlRuleCreateFields: allOf: + - $ref: '#/components/schemas/EsqlRuleOptionalFields' - $ref: '#/components/schemas/EsqlRuleRequiredFields' EsqlRule: diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/import_rules/rule_to_import.ts b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/import_rules/rule_to_import.ts index 9634d773b121d..9dc1e218f6ceb 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/import_rules/rule_to_import.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/import_rules/rule_to_import.ts @@ -9,6 +9,7 @@ import * as z from 'zod'; import { BaseCreateProps, ResponseFields, + RequiredFieldInput, RuleSignatureId, TypeSpecificCreateProps, } from '../../model/rule_schema'; @@ -29,5 +30,13 @@ export const RuleToImport = BaseCreateProps.and(TypeSpecificCreateProps).and( ResponseFields.partial().extend({ rule_id: RuleSignatureId, immutable: z.literal(false).default(false), + /* + Overriding `required_fields` from ResponseFields because + in ResponseFields `required_fields` has the output type, + but for importing rules, we need to use the input type. + Otherwise importing rules without the "ecs" property in + `required_fields` will fail. + */ + required_fields: z.array(RequiredFieldInput).optional(), }) ); diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.gen.ts index b037534248042..f17cb2a5ee0fb 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.gen.ts +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.gen.ts @@ -155,8 +155,8 @@ export const RiskScoreWeightGlobalShared = z.object({ type: z.literal('global_identifier'), }); -export type RiskScoreWeightGlobal = z.infer; -export const RiskScoreWeightGlobal = z.union([ +export type RiskScoreWeight = z.infer; +export const RiskScoreWeight = z.union([ RiskScoreWeightGlobalShared.merge( z.object({ host: RiskScoreEntityIdentifierWeights, @@ -171,34 +171,6 @@ export const RiskScoreWeightGlobal = z.union([ ), ]); -export type RiskScoreWeightCategoryShared = z.infer; -export const RiskScoreWeightCategoryShared = z.object({ - type: z.literal('risk_category'), - value: RiskScoreCategories, -}); - -export type RiskScoreWeightCategory = z.infer; -export const RiskScoreWeightCategory = z.union([ - RiskScoreWeightCategoryShared.merge( - z.object({ - host: RiskScoreEntityIdentifierWeights, - user: RiskScoreEntityIdentifierWeights.optional(), - }) - ), - RiskScoreWeightCategoryShared.merge( - z.object({ - host: RiskScoreEntityIdentifierWeights.optional(), - user: RiskScoreEntityIdentifierWeights, - }) - ), -]); - -/** - * Configuration used to tune risk scoring. Weights can be used to change the score contribution of risk inputs for hosts and users at both a global level and also for Risk Input categories (e.g. 'category_1'). - */ -export type RiskScoreWeight = z.infer; -export const RiskScoreWeight = z.union([RiskScoreWeightGlobal, RiskScoreWeightCategory]); - /** * A list of weights to be applied to the scoring calculation. */ diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.schema.yaml index 7b6634876d8a6..63aa739d2133d 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.schema.yaml @@ -201,7 +201,7 @@ components: enum: - global_identifier - RiskScoreWeightGlobal: + RiskScoreWeight: oneOf: - allOf: - $ref: '#/components/schemas/RiskScoreWeightGlobalShared' @@ -225,65 +225,12 @@ components: user: $ref: '#/components/schemas/RiskScoreEntityIdentifierWeights' - RiskScoreWeightCategoryShared: - x-inline: true - type: object - required: - - type - - value - properties: - type: - type: string - enum: - - risk_category - value: - $ref: '#/components/schemas/RiskScoreCategories' - - RiskScoreWeightCategory: - oneOf: - - allOf: - - $ref: '#/components/schemas/RiskScoreWeightCategoryShared' - - type: object - required: - - host - properties: - host: - $ref: '#/components/schemas/RiskScoreEntityIdentifierWeights' - user: - $ref: '#/components/schemas/RiskScoreEntityIdentifierWeights' - - - allOf: - - $ref: '#/components/schemas/RiskScoreWeightCategoryShared' - - type: object - required: - - user - properties: - host: - $ref: '#/components/schemas/RiskScoreEntityIdentifierWeights' - user: - $ref: '#/components/schemas/RiskScoreEntityIdentifierWeights' - - RiskScoreWeight: - description: "Configuration used to tune risk scoring. Weights can be used to change the score contribution of risk inputs for hosts and users at both a global level and also for Risk Input categories (e.g. 'category_1')." - oneOf: - - $ref: '#/components/schemas/RiskScoreWeightGlobal' - - $ref: '#/components/schemas/RiskScoreWeightCategory' - example: - type: 'risk_category' - value: 'category_1' - host: 0.8 - user: 0.4 - RiskScoreWeights: description: 'A list of weights to be applied to the scoring calculation.' type: array items: $ref: '#/components/schemas/RiskScoreWeight' example: - - type: 'risk_category' - value: 'category_1' - host: 0.8 - user: 0.4 - type: 'global_identifier' host: 0.5 user: 0.1 diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/common/risk_weights.schema.test.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/common/risk_weights.schema.test.ts index 59b0859300f88..e4afc38badd24 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/common/risk_weights.schema.test.ts +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/common/risk_weights.schema.test.ts @@ -59,9 +59,7 @@ describe('risk weight schema', () => { const decoded = RiskScoreWeight.safeParse(payload) as SafeParseError; expect(decoded.success).toBeFalsy(); - expect(stringifyZodError(decoded.error)).toEqual( - 'host: Required, user: Required, type: Invalid literal value, expected "risk_category", value: Invalid literal value, expected "category_1", host: Required, and 3 more' - ); + expect(stringifyZodError(decoded.error)).toContain('host: Required, user: Required'); }); it('allows a single host weight', () => { @@ -123,44 +121,10 @@ describe('risk weight schema', () => { expect(decoded.success).toBeFalsy(); expect(stringifyZodError(decoded.error)).toEqual( - 'type: Invalid literal value, expected "global_identifier", host: Required, type: Invalid literal value, expected "global_identifier", value: Invalid literal value, expected "category_1", host: Required, and 1 more' + 'type: Invalid literal value, expected "global_identifier", host: Required, type: Invalid literal value, expected "global_identifier"' ); }); - it('rejects if neither host nor user weight are specified', () => { - const payload = { type, value: RiskCategories.category_1 }; - const decoded = RiskScoreWeight.safeParse(payload) as SafeParseError; - - expect(decoded.success).toBeFalsy(); - expect(stringifyZodError(decoded.error)).toEqual( - 'type: Invalid literal value, expected "global_identifier", host: Required, type: Invalid literal value, expected "global_identifier", user: Required, host: Required, and 1 more' - ); - }); - - it('allows a single host weight', () => { - const payload = { type, value: RiskCategories.category_1, host: 0.1 }; - const decoded = RiskScoreWeight.safeParse(payload) as SafeParseSuccess; - - expect(decoded.success).toBeTruthy(); - expect(decoded.data).toEqual(payload); - }); - - it('allows a single user weight', () => { - const payload = { type, value: RiskCategories.category_1, user: 0.1 }; - const decoded = RiskScoreWeight.safeParse(payload) as SafeParseSuccess; - - expect(decoded.success).toBeTruthy(); - expect(decoded.data).toEqual(payload); - }); - - it('allows both a host and user weight', () => { - const payload = { type, value: RiskCategories.category_1, user: 0.1, host: 0.5 }; - const decoded = RiskScoreWeight.safeParse(payload) as SafeParseSuccess; - - expect(decoded.success).toBeTruthy(); - expect(decoded.data).toEqual(payload); - }); - it('rejects a weight outside of 0-1', () => { const payload = { type, value: RiskCategories.category_1, host: -5 }; const decoded = RiskScoreWeight.safeParse(payload) as SafeParseError; @@ -170,47 +134,6 @@ describe('risk weight schema', () => { `host: Number must be greater than or equal to 0` ); }); - - it('removes extra keys if specified', () => { - const payload = { - type, - value: RiskCategories.category_1, - host: 0.1, - extra: 'even more', - }; - const decoded = RiskScoreWeight.safeParse(payload) as SafeParseSuccess; - - expect(decoded.success).toBeTruthy(); - expect(decoded.data).toEqual({ type, value: RiskCategories.category_1, host: 0.1 }); - }); - - describe('allowed category values', () => { - it('allows the alerts type for a category', () => { - const payload = { - type, - value: RiskCategories.category_1, - host: 0.1, - }; - const decoded = RiskScoreWeight.safeParse(payload) as SafeParseSuccess; - - expect(decoded.success).toBeTruthy(); - expect(decoded.data).toEqual(payload); - }); - - it('rejects an unknown category value', () => { - const payload = { - type, - value: 'unknown', - host: 0.1, - }; - const decoded = RiskScoreWeight.safeParse(payload) as SafeParseError; - - expect(decoded.success).toBeFalsy(); - expect(stringifyZodError(decoded.error)).toContain( - 'type: Invalid literal value, expected "global_identifier", type: Invalid literal value, expected "global_identifier", user: Required, value: Invalid literal value, expected "category_1", value: Invalid literal value, expected "category_1", and 1 more' - ); - }); - }); }); }); }); diff --git a/x-pack/plugins/security_solution/common/detection_engine/constants.ts b/x-pack/plugins/security_solution/common/detection_engine/constants.ts index b3bc1f434ff51..54c81cf93568f 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/constants.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/constants.ts @@ -41,6 +41,7 @@ export const MINIMUM_LICENSE_FOR_SUPPRESSION = 'platinum' as const; export const SUPPRESSIBLE_ALERT_RULES: Type[] = [ 'threshold', + 'esql', 'saved_query', 'query', 'new_terms', diff --git a/x-pack/plugins/security_solution/common/detection_engine/utils.test.ts b/x-pack/plugins/security_solution/common/detection_engine/utils.test.ts index e0c76253c416a..2e5ac39936fa3 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/utils.test.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/utils.test.ts @@ -229,6 +229,7 @@ describe('Alert Suppression Rules', () => { describe('isSuppressibleAlertRule', () => { test('should return true for a suppressible rule type', () => { // Rule types that support alert suppression: + expect(isSuppressibleAlertRule('esql')).toBe(true); expect(isSuppressibleAlertRule('threshold')).toBe(true); expect(isSuppressibleAlertRule('saved_query')).toBe(true); expect(isSuppressibleAlertRule('query')).toBe(true); @@ -238,7 +239,6 @@ describe('Alert Suppression Rules', () => { // Rule types that don't support alert suppression: expect(isSuppressibleAlertRule('machine_learning')).toBe(false); - expect(isSuppressibleAlertRule('esql')).toBe(false); }); test('should return false for an unknown rule type', () => { @@ -266,6 +266,7 @@ describe('Alert Suppression Rules', () => { describe('isSuppressionRuleConfiguredWithDuration', () => { test('should return true for a suppressible rule type', () => { // Rule types that support alert suppression: + expect(isSuppressionRuleConfiguredWithDuration('esql')).toBe(true); expect(isSuppressionRuleConfiguredWithDuration('threshold')).toBe(true); expect(isSuppressionRuleConfiguredWithDuration('saved_query')).toBe(true); expect(isSuppressionRuleConfiguredWithDuration('query')).toBe(true); @@ -275,7 +276,6 @@ describe('Alert Suppression Rules', () => { // Rule types that don't support alert suppression: expect(isSuppressionRuleConfiguredWithDuration('machine_learning')).toBe(false); - expect(isSuppressionRuleConfiguredWithDuration('esql')).toBe(false); }); test('should return false for an unknown rule type', () => { @@ -288,6 +288,7 @@ describe('Alert Suppression Rules', () => { describe('isSuppressionRuleConfiguredWithGroupBy', () => { test('should return true for a suppressible rule type with groupBy', () => { // Rule types that support alert suppression groupBy: + expect(isSuppressionRuleConfiguredWithGroupBy('esql')).toBe(true); expect(isSuppressionRuleConfiguredWithGroupBy('saved_query')).toBe(true); expect(isSuppressionRuleConfiguredWithGroupBy('query')).toBe(true); expect(isSuppressionRuleConfiguredWithGroupBy('threat_match')).toBe(true); @@ -296,7 +297,6 @@ describe('Alert Suppression Rules', () => { // Rule types that don't support alert suppression: expect(isSuppressionRuleConfiguredWithGroupBy('machine_learning')).toBe(false); - expect(isSuppressionRuleConfiguredWithGroupBy('esql')).toBe(false); }); test('should return false for a threshold rule type', () => { @@ -314,6 +314,7 @@ describe('Alert Suppression Rules', () => { describe('isSuppressionRuleConfiguredWithMissingFields', () => { test('should return true for a suppressible rule type with missing fields', () => { // Rule types that support alert suppression groupBy: + expect(isSuppressionRuleConfiguredWithMissingFields('esql')).toBe(true); expect(isSuppressionRuleConfiguredWithMissingFields('saved_query')).toBe(true); expect(isSuppressionRuleConfiguredWithMissingFields('query')).toBe(true); expect(isSuppressionRuleConfiguredWithMissingFields('threat_match')).toBe(true); @@ -322,7 +323,6 @@ describe('Alert Suppression Rules', () => { // Rule types that don't support alert suppression: expect(isSuppressionRuleConfiguredWithMissingFields('machine_learning')).toBe(false); - expect(isSuppressionRuleConfiguredWithMissingFields('esql')).toBe(false); }); test('should return false for a threshold rule type', () => { diff --git a/x-pack/plugins/security_solution/common/endpoint/data_loaders/setup_fleet_for_endpoint.ts b/x-pack/plugins/security_solution/common/endpoint/data_loaders/setup_fleet_for_endpoint.ts index 12505dc87a2b0..e9da1b9a4df8c 100644 --- a/x-pack/plugins/security_solution/common/endpoint/data_loaders/setup_fleet_for_endpoint.ts +++ b/x-pack/plugins/security_solution/common/endpoint/data_loaders/setup_fleet_for_endpoint.ts @@ -29,10 +29,6 @@ import { wrapErrorAndRejectPromise, } from './utils'; -export interface SetupFleetForEndpointResponse { - endpointPackage: BulkInstallPackageInfo; -} - /** * Calls the fleet setup APIs and then installs the latest Endpoint package * @param kbnClient @@ -43,7 +39,7 @@ export const setupFleetForEndpoint = usageTracker.track( async (kbnClient: KbnClient, logger?: ToolingLog): Promise => { const log = logger ?? createToolingLogger(); - log.info(`setupFleetForEndpoint(): Setting up fleet for endpoint`); + log.debug(`setupFleetForEndpoint(): Setting up fleet for endpoint`); // Setup Fleet try { diff --git a/x-pack/plugins/security_solution/common/endpoint/format_axios_error.ts b/x-pack/plugins/security_solution/common/endpoint/format_axios_error.ts index 1f0c7da3bbad6..fa46f7940c17e 100644 --- a/x-pack/plugins/security_solution/common/endpoint/format_axios_error.ts +++ b/x-pack/plugins/security_solution/common/endpoint/format_axios_error.ts @@ -22,15 +22,18 @@ export class FormattedAxiosError extends Error { }; constructor(axiosError: AxiosError) { + const method = axiosError.config?.method ?? ''; + const url = axiosError.config?.url ?? ''; + super( `${axiosError.message}${ axiosError?.response?.data ? `: ${JSON.stringify(axiosError?.response?.data)}` : '' - }` + }${url ? `\n(Request: ${method} ${url})` : ''}` ); this.request = { - method: axiosError.config?.method ?? '?', - url: axiosError.config?.url ?? '?', + method, + url, data: axiosError.config?.data ?? '', }; 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 7eed9b47fb2d3..7b33c5a3606a5 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/index.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/index.ts @@ -1123,7 +1123,7 @@ export interface BlocklistFields { } export interface OnWriteScanFields { - on_write_scan?: boolean; + on_write_scan: boolean; } /** Policy protection mode options */ diff --git a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/constants.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/constants.ts index dc23dceadd642..5a0a6541791ba 100644 --- a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/constants.ts +++ b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/constants.ts @@ -23,7 +23,8 @@ export const RISK_ENGINE_REQUIRED_ES_CLUSTER_PRIVILEGES = [ export const RISK_SCORE_INDEX_PATTERN = 'risk-score.risk-score-*'; -type RiskEngineIndexPrivilege = 'read' | 'write'; +export type RiskEngineIndexPrivilege = 'read' | 'write'; + export const RISK_ENGINE_REQUIRED_ES_INDEX_PRIVILEGES = Object.freeze({ [RISK_SCORE_INDEX_PATTERN]: ['read', 'write'] as RiskEngineIndexPrivilege[], }); diff --git a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/privileges.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/privileges.ts index a375f5cb9195b..b0bbc39609b3b 100644 --- a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/privileges.ts +++ b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/privileges.ts @@ -5,7 +5,9 @@ * 2.0. */ +import type { NonEmptyArray } from 'fp-ts/NonEmptyArray'; import type { EntityAnalyticsPrivileges } from '../../api/entity_analytics/asset_criticality/get_asset_criticality_privileges.gen'; +import type { RiskEngineIndexPrivilege } from './constants'; import { RISK_ENGINE_REQUIRED_ES_CLUSTER_PRIVILEGES, RISK_ENGINE_REQUIRED_ES_INDEX_PRIVILEGES, @@ -20,7 +22,8 @@ export interface MissingPrivileges { } export const getMissingIndexPrivileges = ( - privileges: EntityAnalyticsPrivileges['privileges']['elasticsearch']['index'] + privileges: EntityAnalyticsPrivileges['privileges']['elasticsearch']['index'], + required: NonEmptyArray = ['read', 'write'] ): MissingIndexPrivileges => { const missingIndexPrivileges: MissingIndexPrivileges = []; @@ -28,10 +31,10 @@ export const getMissingIndexPrivileges = ( return missingIndexPrivileges; } - for (const [indexName, requiredPrivileges] of Object.entries( + for (const [indexName, defaultRequiredPrivileges] of Object.entries( RISK_ENGINE_REQUIRED_ES_INDEX_PRIVILEGES )) { - const missingPrivileges = requiredPrivileges.filter( + const missingPrivileges = (required || defaultRequiredPrivileges).filter( (privilege) => !privileges[indexName][privilege] ); @@ -44,9 +47,13 @@ export const getMissingIndexPrivileges = ( }; export const getMissingRiskEnginePrivileges = ( - privileges: EntityAnalyticsPrivileges['privileges'] + privileges: EntityAnalyticsPrivileges['privileges'], + required?: NonEmptyArray ): MissingPrivileges => { - const missingIndexPrivileges = getMissingIndexPrivileges(privileges.elasticsearch.index); + const missingIndexPrivileges = getMissingIndexPrivileges( + privileges.elasticsearch.index, + required + ); const missingClusterPrivileges = RISK_ENGINE_REQUIRED_ES_CLUSTER_PRIVILEGES.filter( (privilege) => !privileges.elasticsearch.cluster?.[privilege] ); diff --git a/x-pack/plugins/security_solution/common/experimental_features.ts b/x-pack/plugins/security_solution/common/experimental_features.ts index 66d9cedd24933..565177fa8b560 100644 --- a/x-pack/plugins/security_solution/common/experimental_features.ts +++ b/x-pack/plugins/security_solution/common/experimental_features.ts @@ -180,6 +180,11 @@ export const allowedExperimentalValues = Object.freeze({ */ disableTimelineSaveTour: false, + /** + * Enables alerts suppression for ES|QL rules + */ + alertSuppressionForEsqlRuleEnabled: false, + /** * Enables the risk engine privileges route * and associated callout in the UI @@ -250,7 +255,12 @@ export const allowedExperimentalValues = Object.freeze({ /** * Makes Elastic Defend integration's Malware On-Write Scan option available to edit. */ - malwareOnWriteScanOptionAvailable: false, + malwareOnWriteScanOptionAvailable: true, + + /** + * Enables unified manifest that replaces existing user artifacts manifest SO with a new approach of creating a SO per package policy. + */ + unifiedManifestEnabled: false, /** * Enables Security AI Assistant's Flyout mode diff --git a/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/installation_and_upgrade.md b/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/installation_and_upgrade.md index 24aaf34764034..beda8a9517830 100644 --- a/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/installation_and_upgrade.md +++ b/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/installation_and_upgrade.md @@ -198,6 +198,9 @@ Examples: │ New Terms │ Custom query │ Overview │ Definition │ │ New Terms │ Filters │ Overview │ Definition │ │ ESQL │ ESQL query │ Overview │ Definition │ +│ ESQL │ Suppress alerts by │ Overview │ Definition │ +│ ESQL │ Suppress alerts for │ Overview │ Definition │ +│ ESQL │ If a suppression field is missing │ Overview │ Definition │ ``` ## Scenarios diff --git a/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/view_in_ai_assistant/use_view_in_ai_assistant.ts b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/view_in_ai_assistant/use_view_in_ai_assistant.ts index 228f882863777..58f66b1b2a0dd 100644 --- a/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/view_in_ai_assistant/use_view_in_ai_assistant.ts +++ b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/view_in_ai_assistant/use_view_in_ai_assistant.ts @@ -12,8 +12,6 @@ import { useAssistantAvailability } from '../../../assistant/use_assistant_avail import { getAttackDiscoveryMarkdown } from '../../get_attack_discovery_markdown/get_attack_discovery_markdown'; import type { AttackDiscovery } from '../../types'; -const useAssistantNoop = () => ({ promptContextId: undefined, showAssistantOverlay: () => {} }); - /** * This category is provided in the prompt context for the assistant */ @@ -25,12 +23,7 @@ export const useViewInAiAssistant = ({ attackDiscovery: AttackDiscovery; replacements?: Replacements; }) => { - const { hasAssistantPrivilege } = useAssistantAvailability(); - - const useAssistantHook = useMemo( - () => (hasAssistantPrivilege ? useAssistantOverlay : useAssistantNoop), - [hasAssistantPrivilege] - ); + const { hasAssistantPrivilege, isAssistantEnabled } = useAssistantAvailability(); // the prompt context for this insight: const getPromptContext = useCallback( @@ -41,7 +34,7 @@ export const useViewInAiAssistant = ({ }), [attackDiscovery] ); - const { promptContextId, showAssistantOverlay: showOverlay } = useAssistantHook( + const { promptContextId, showAssistantOverlay: showOverlay } = useAssistantOverlay( category, attackDiscovery.title, // conversation title attackDiscovery.title, // description used in context pill @@ -49,6 +42,7 @@ export const useViewInAiAssistant = ({ attackDiscovery.id, // accept the UUID default for this prompt context null, // suggestedUserPrompt null, // tooltip + isAssistantEnabled, replacements ?? null ); diff --git a/x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/countdown/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/countdown/index.tsx index e49050c54c954..8a61704ef7361 100644 --- a/x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/countdown/index.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/countdown/index.tsx @@ -9,6 +9,7 @@ import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, + EuiOutsideClickDetector, EuiPopover, EuiText, useEuiTheme, @@ -80,15 +81,17 @@ const CountdownComponent: React.FC = ({ approximateFutureTime, connectorI justifyContent="spaceBetween" > - - - + closePopover()}> + + + + diff --git a/x-pack/plugins/security_solution/public/attack_discovery/pages/session_storage/index.test.ts b/x-pack/plugins/security_solution/public/attack_discovery/pages/session_storage/index.test.ts new file mode 100644 index 0000000000000..dd5932bbb3dd7 --- /dev/null +++ b/x-pack/plugins/security_solution/public/attack_discovery/pages/session_storage/index.test.ts @@ -0,0 +1,180 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { GenerationInterval } from '../../types'; +import { + encodeGenerationIntervals, + decodeGenerationIntervals, + getLocalStorageGenerationIntervals, + setLocalStorageGenerationIntervals, +} from '.'; + +const key = 'elasticAssistantDefault.attackDiscovery.default.generationIntervals'; + +const generationIntervals: Record = { + 'test-connector-1': [ + { + connectorId: 'test-connector-1', + date: new Date('2024-05-16T14:13:09.838Z'), + durationMs: 173648, + }, + { + connectorId: 'test-connector-1', + date: new Date('2024-05-16T13:59:49.620Z'), + durationMs: 146605, + }, + { + connectorId: 'test-connector-1', + date: new Date('2024-05-16T13:47:00.629Z'), + durationMs: 255163, + }, + ], + testConnector2: [ + { + connectorId: 'testConnector2', + date: new Date('2024-05-16T14:26:25.273Z'), + durationMs: 130447, + }, + ], + testConnector3: [ + { + connectorId: 'testConnector3', + date: new Date('2024-05-16T14:36:53.171Z'), + durationMs: 46614, + }, + { + connectorId: 'testConnector3', + date: new Date('2024-05-16T14:27:17.187Z'), + durationMs: 44129, + }, + ], +}; + +describe('storage', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('encodeGenerationIntervals', () => { + it('returns null when generationIntervals is invalid', () => { + const invalidGenerationIntervals: Record = + 1n as unknown as Record; // <-- invalid + + const result = encodeGenerationIntervals(invalidGenerationIntervals); + + expect(result).toBeNull(); + }); + + it('returns the expected encoded generationIntervals', () => { + const result = encodeGenerationIntervals(generationIntervals); + + expect(result).toEqual(JSON.stringify(generationIntervals)); + }); + }); + + describe('decodeGenerationIntervals', () => { + it('returns null when generationIntervals is invalid', () => { + const invalidGenerationIntervals = 'invalid generation intervals'; // <-- invalid + + const result = decodeGenerationIntervals(invalidGenerationIntervals); + + expect(result).toBeNull(); + }); + + it('returns the expected decoded generation intervals', () => { + const encoded = encodeGenerationIntervals(generationIntervals) ?? ''; // <-- valid intervals + + const result = decodeGenerationIntervals(encoded); + + expect(result).toEqual(generationIntervals); + }); + + it('parses date strings into Date objects', () => { + const encoded = JSON.stringify({ + 'test-connector-1': [ + { + connectorId: 'test-connector-1', + date: '2024-05-16T14:13:09.838Z', + durationMs: 173648, + }, + ], + }); + + const result = decodeGenerationIntervals(encoded); + + expect(result).toEqual({ + 'test-connector-1': [ + { + connectorId: 'test-connector-1', + date: new Date('2024-05-16T14:13:09.838Z'), + durationMs: 173648, + }, + ], + }); + }); + + it('returns null when date is not a string', () => { + const encoded = JSON.stringify({ + 'test-connector-1': [ + { + connectorId: 'test-connector-1', + date: 1234, // <-- invalid + durationMs: 173648, + }, + ], + }); + + const result = decodeGenerationIntervals(encoded); + + expect(result).toBeNull(); + }); + }); + + describe('getLocalStorageGenerationIntervals', () => { + it('returns null when the key is empty', () => { + const result = getLocalStorageGenerationIntervals(''); // <-- empty key + + expect(result).toBeNull(); + }); + + it('returns null the key is unknown', () => { + const result = getLocalStorageGenerationIntervals('unknown key'); // <-- unknown key + + expect(result).toBeNull(); + }); + + it('returns null when the generation intervals are invalid', () => { + localStorage.setItem(key, 'invalid generation intervals'); // <-- invalid + + const result = getLocalStorageGenerationIntervals(key); + + expect(result).toBeNull(); + }); + + it('returns the expected decoded generation intervals', () => { + const encoded = encodeGenerationIntervals(generationIntervals) ?? ''; // <-- valid intervals + localStorage.setItem(key, encoded); + + const decoded = decodeGenerationIntervals(encoded); + const result = getLocalStorageGenerationIntervals(key); + + expect(result).toEqual(decoded); + }); + }); + + describe('setLocalStorageGenerationIntervals', () => { + const localStorageSetItemSpy = jest.spyOn(Storage.prototype, 'setItem'); + + it('sets the encoded generation intervals in localStorage', () => { + const encoded = encodeGenerationIntervals(generationIntervals) ?? ''; + + setLocalStorageGenerationIntervals({ key, generationIntervals }); + + expect(localStorageSetItemSpy).toHaveBeenCalledWith(key, encoded); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/attack_discovery/pages/session_storage/index.ts b/x-pack/plugins/security_solution/public/attack_discovery/pages/session_storage/index.ts index c959374167504..8c8c49b482650 100644 --- a/x-pack/plugins/security_solution/public/attack_discovery/pages/session_storage/index.ts +++ b/x-pack/plugins/security_solution/public/attack_discovery/pages/session_storage/index.ts @@ -76,8 +76,18 @@ export const encodeGenerationIntervals = ( export const decodeGenerationIntervals = ( generationIntervals: string ): Record | null => { + const parseDate = (key: string, value: unknown) => { + if (key === 'date' && typeof value === 'string') { + return new Date(value); + } else if (key === 'date' && typeof value !== 'string') { + throw new Error('Invalid date'); + } else { + return value; + } + }; + try { - return JSON.parse(generationIntervals); + return JSON.parse(generationIntervals, parseDate); } catch { return null; } @@ -87,7 +97,7 @@ export const getLocalStorageGenerationIntervals = ( key: string ): Record | null => { if (!isEmpty(key)) { - return decodeGenerationIntervals(sessionStorage.getItem(key) ?? ''); + return decodeGenerationIntervals(localStorage.getItem(key) ?? ''); } return null; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/related_integrations/related_integration_field.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/related_integrations/related_integration_field.tsx index c24220923441b..fb6e89fb44acc 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/related_integrations/related_integration_field.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/related_integrations/related_integration_field.tsx @@ -24,7 +24,6 @@ import type { FieldHook } from '../../../../shared_imports'; import type { Integration, RelatedIntegration } from '../../../../../common/api/detection_engine'; import { useIntegrations } from '../../../../detections/components/rules/related_integrations/use_integrations'; import { IntegrationStatusBadge } from './integration_status_badge'; -import { DEFAULT_RELATED_INTEGRATION } from './default_related_integration'; import * as i18n from './translations'; interface RelatedIntegrationItemFormProps { @@ -95,16 +94,6 @@ export function RelatedIntegrationField({ ); const hasError = Boolean(packageErrorMessage) || Boolean(versionErrorMessage); - const isLastField = relatedIntegrations.length === 1; - const isLastEmptyField = isLastField && field.value.package === ''; - const handleRemove = useCallback(() => { - if (isLastField) { - field.setValue(DEFAULT_RELATED_INTEGRATION); - return; - } - - onRemove(); - }, [onRemove, field, isLastField]); return ( ({ docLinks: { links: { securitySolution: { - ruleUiAdvancedParams: 'http://link-to-docs', + createDetectionRules: 'http://link-to-docs', }, }, }, @@ -669,82 +669,6 @@ describe('RelatedIntegrations form part', () => { }); }); }); - - describe('sticky last form row', () => { - it('does not remove the last item', async () => { - render(, { wrapper: createReactQueryWrapper() }); - - await addRelatedIntegrationRow(); - await removeLastRelatedIntegrationRow(); - - expect(screen.getAllByTestId(RELATED_INTEGRATION_ROW)).toHaveLength(1); - }); - - it('disables remove button after clicking remove button on the last item', async () => { - render(, { wrapper: createReactQueryWrapper() }); - - await addRelatedIntegrationRow(); - await removeLastRelatedIntegrationRow(); - - expect(screen.getByTestId(REMOVE_INTEGRATION_ROW_BUTTON_TEST_ID)).toBeDisabled(); - }); - - it('clears selected integration when clicking remove the last form row button', async () => { - render(, { wrapper: createReactQueryWrapper() }); - - await addRelatedIntegrationRow(); - await selectFirstEuiComboBoxOption({ - comboBoxToggleButton: getLastByTestId(COMBO_BOX_TOGGLE_BUTTON_TEST_ID), - }); - await removeLastRelatedIntegrationRow(); - - expect(screen.queryByTestId(COMBO_BOX_SELECTION_TEST_ID)).not.toBeInTheDocument(); - }); - - it('submits an empty integration after clicking remove the last form row button', async () => { - const handleSubmit = jest.fn(); - - render(, { wrapper: createReactQueryWrapper() }); - - await addRelatedIntegrationRow(); - await selectFirstEuiComboBoxOption({ - comboBoxToggleButton: getLastByTestId(COMBO_BOX_TOGGLE_BUTTON_TEST_ID), - }); - await removeLastRelatedIntegrationRow(); - await submitForm(); - await waitFor(() => { - expect(handleSubmit).toHaveBeenCalled(); - }); - - expect(handleSubmit).toHaveBeenCalledWith({ - data: [{ package: '', version: '' }], - isValid: true, - }); - }); - - it('submits an empty integration after previously saved integrations were removed', async () => { - const initialRelatedIntegrations: RelatedIntegration[] = [ - { package: 'package-a', version: '^1.2.3' }, - ]; - const handleSubmit = jest.fn(); - - render(, { - wrapper: createReactQueryWrapper(), - }); - - await waitForIntegrationsToBeLoaded(); - await removeLastRelatedIntegrationRow(); - await submitForm(); - await waitFor(() => { - expect(handleSubmit).toHaveBeenCalled(); - }); - - expect(handleSubmit).toHaveBeenCalledWith({ - data: [{ package: '', version: '' }], - isValid: true, - }); - }); - }); }); }); @@ -778,11 +702,6 @@ function TestForm({ initialState, onSubmit }: TestFormProps): JSX.Element { ); } -function getLastByTestId(testId: string): HTMLElement { - // getAllByTestId throws an error when there are no `testId` elements found - return screen.getAllByTestId(testId).at(-1)!; -} - function waitForIntegrationsToBeLoaded(): Promise { return waitForElementToBeRemoved(screen.queryAllByRole('progressbar')); } diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/related_integrations/related_integrations_help_info.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/related_integrations/related_integrations_help_info.tsx index b694d17a80435..08c4a8e22edfd 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/related_integrations/related_integrations_help_info.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/related_integrations/related_integrations_help_info.tsx @@ -40,7 +40,7 @@ export function RelatedIntegrationsHelpInfo(): JSX.Element { defaultMessage="Choose the {integrationsDocLink} this rule depends on, and correct if necessary each integration’s version constraint in {semverLink} format. Only tilde, caret, and plain versions are supported, such as ~1.2.3, ^1.2.3, or 1.2.3." values={{ integrationsDocLink: ( - + > + ): ReturnType> | undefined { + const [{ value, path, form }] = args; + + const formData = form.getFormData(); + const parentFieldData: RequiredFieldInput[] = formData[parentFieldPath]; + + const isFieldNameUsedMoreThanOnce = + parentFieldData.filter((field) => field.name === value.name).length > 1; + + if (isFieldNameUsedMoreThanOnce) { + return { + code: 'ERR_FIELD_FORMAT', + path: `${path}.name`, + message: i18n.FIELD_NAME_USED_MORE_THAN_ONCE(value.name), + }; + } + + /* Allow empty rows. They are going to be removed before submission. */ + if (value.name.trim().length === 0 && value.type.trim().length === 0) { + return; + } + + if (value.name.trim().length === 0) { + return { + code: 'ERR_FIELD_MISSING', + path: `${path}.name`, + message: i18n.FIELD_NAME_REQUIRED, + }; + } + + if (value.type.trim().length === 0) { + return { + code: 'ERR_FIELD_MISSING', + path: `${path}.type`, + message: i18n.FIELD_TYPE_REQUIRED, + }; + } + }; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/name_combobox.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/name_combobox.tsx new file mode 100644 index 0000000000000..848e3c9a3c558 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/name_combobox.tsx @@ -0,0 +1,126 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback, useMemo, useState, useEffect } from 'react'; +import { EuiComboBox, EuiIcon } from '@elastic/eui'; +import type { EuiComboBoxOptionOption } from '@elastic/eui'; +import { euiThemeVars } from '@kbn/ui-theme'; +import type { FieldHook } from '../../../../shared_imports'; +import type { RequiredFieldInput } from '../../../../../common/api/detection_engine/model/rule_schema/common_attributes.gen'; +import { pickTypeForName } from './utils'; +import * as i18n from './translations'; + +interface NameComboBoxProps { + field: FieldHook; + itemId: string; + availableFieldNames: string[]; + typesByFieldName: Record; + nameWarning: string; + nameError: { message: string } | undefined; +} + +export function NameComboBox({ + field, + itemId, + availableFieldNames, + typesByFieldName, + nameWarning, + nameError, +}: NameComboBoxProps) { + const { value, setValue } = field; + + const selectableNameOptions: Array> = useMemo( + () => + /* Not adding an empty string to the list of selectable field names */ + (value.name ? [value.name] : []).concat(availableFieldNames).map((name) => ({ + label: name, + value: name, + })), + [availableFieldNames, value.name] + ); + + /* + Using a state for `selectedNameOptions` instead of using the field value directly + to fix the issue where pressing the backspace key in combobox input would clear the field value + and trigger a validation error. By using a separate state, we can clear the selected option + without clearing the field value. + */ + const [selectedNameOption, setSelectedNameOption] = useState< + EuiComboBoxOptionOption | undefined + >(selectableNameOptions.find((option) => option.label === value.name)); + + useEffect(() => { + /* Re-computing the new selected name option when the field value changes */ + setSelectedNameOption(selectableNameOptions.find((option) => option.label === value.name)); + }, [value.name, selectableNameOptions]); + + const handleNameChange = useCallback( + (selectedOptions: Array>) => { + const newlySelectedOption: EuiComboBoxOptionOption | undefined = selectedOptions[0]; + + if (!newlySelectedOption) { + /* This occurs when the user hits backspace in combobox */ + setSelectedNameOption(undefined); + return; + } + + const updatedName = newlySelectedOption?.value || ''; + + const updatedType = pickTypeForName({ + name: updatedName, + type: value.type, + typesByFieldName, + }); + + const updatedFieldValue: RequiredFieldInput = { + name: updatedName, + type: updatedType, + }; + + setValue(updatedFieldValue); + }, + [setValue, value.type, typesByFieldName] + ); + + const handleAddCustomName = useCallback( + (newName: string) => { + const updatedFieldValue: RequiredFieldInput = { + name: newName, + type: pickTypeForName({ name: newName, type: value.type, typesByFieldName }), + }; + + setValue(updatedFieldValue); + }, + [setValue, value.type, typesByFieldName] + ); + + return ( + + ) : undefined + } + /> + ); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields.test.tsx new file mode 100644 index 0000000000000..2812c147d9c2d --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields.test.tsx @@ -0,0 +1,724 @@ +/* + * Copyright 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 { I18nProvider } from '@kbn/i18n-react'; +import { screen, render, act, fireEvent, waitFor } from '@testing-library/react'; +import { Form, useForm } from '../../../../shared_imports'; + +import type { DataViewFieldBase } from '@kbn/es-query'; +import { RequiredFields } from './required_fields'; +import type { RequiredFieldInput } from '../../../../../common/api/detection_engine'; + +const ADD_REQUIRED_FIELD_BUTTON_TEST_ID = 'addRequiredFieldButton'; +const REQUIRED_FIELDS_GENERAL_WARNING_TEST_ID = 'requiredFieldsGeneralWarning'; + +describe('RequiredFields form part', () => { + it('displays the required fields label', () => { + render(); + + expect(screen.getByText('Required fields')); + }); + + it('displays previously saved required fields', () => { + const initialState = [ + { name: 'field1', type: 'string' }, + { name: 'field2', type: 'number' }, + ]; + + render(); + + expect(screen.getByDisplayValue('field1')).toBeVisible(); + expect(screen.getByDisplayValue('string')).toBeVisible(); + + expect(screen.getByDisplayValue('field2')).toBeVisible(); + expect(screen.getByDisplayValue('number')).toBeVisible(); + }); + + it('user can add a new required field to an empty form', async () => { + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string'] }), + ]; + + render(); + + await addRequiredFieldRow(); + await selectFirstEuiComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForName('empty'), + }); + + expect(screen.getByDisplayValue('field1')).toBeVisible(); + expect(screen.getByDisplayValue('string')).toBeVisible(); + }); + + it('user can add a new required field to a previously saved form', async () => { + const initialState = [{ name: 'field1', type: 'string' }]; + + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field2', esTypes: ['keyword'] }), + ]; + + render(); + + await addRequiredFieldRow(); + + await selectFirstEuiComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForName('empty'), + }); + + expect(screen.getByDisplayValue('field2')).toBeVisible(); + expect(screen.getByDisplayValue('keyword')).toBeVisible(); + }); + + it('user can select any field name that is available in index patterns', async () => { + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string'] }), + createIndexPatternField({ name: 'field2', esTypes: ['keyword'] }), + ]; + + render(); + + await addRequiredFieldRow(); + + await selectEuiComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForName('empty'), + optionIndex: 0, + }); + + expect(screen.getByDisplayValue('field1')).toBeVisible(); + expect(screen.getByDisplayValue('string')).toBeVisible(); + + await addRequiredFieldRow(); + + await selectEuiComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForName('empty'), + optionIndex: 0, + }); + + expect(screen.getByDisplayValue('field2')).toBeVisible(); + expect(screen.getByDisplayValue('keyword')).toBeVisible(); + }); + + it('user can add his own custom field name and type', async () => { + render(); + + await addRequiredFieldRow(); + + await typeInCustomComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForName('empty'), + optionText: 'customField', + }); + + expect(screen.getByDisplayValue('customField')).toBeVisible(); + + await typeInCustomComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForType('empty'), + optionText: 'customType', + }); + + expect(screen.getByDisplayValue('customType')).toBeVisible(); + expect(screen.queryByTestId(REQUIRED_FIELDS_GENERAL_WARNING_TEST_ID)).toBeVisible(); + }); + + it('field type dropdown allows to choose from options if multiple types are available', async () => { + const initialState = [{ name: 'field1', type: 'string' }]; + + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string', 'keyword'] }), + ]; + + render(); + + await selectEuiComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForType('string'), + optionText: 'keyword', + }); + + expect(screen.getByDisplayValue('keyword')).toBeVisible(); + }); + + it('user can remove a required field', async () => { + const initialState = [{ name: 'field1', type: 'string' }]; + + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string'] }), + ]; + + render(); + + await act(async () => { + fireEvent.click(screen.getByTestId('removeRequiredFieldButton-field1')); + }); + + expect(screen.queryByDisplayValue('field1')).toBeNull(); + }); + + it('user can not select the same field twice', async () => { + const initialState = [{ name: 'field1', type: 'string' }]; + + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string'] }), + createIndexPatternField({ name: 'field2', esTypes: ['keyword'] }), + createIndexPatternField({ name: 'field3', esTypes: ['date'] }), + ]; + + render(); + + await addRequiredFieldRow(); + + const emptyRowOptions = await getDropdownOptions(getSelectToggleButtonForName('empty')); + expect(emptyRowOptions).toEqual(['field2', 'field3']); + + await selectEuiComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForName('empty'), + optionText: 'field2', + }); + + const firstRowNameOptions = await getDropdownOptions(getSelectToggleButtonForName('field1')); + expect(firstRowNameOptions).toEqual(['field1', 'field3']); + }); + + it('adding a new required field is disabled when index patterns are loading', async () => { + render(); + + expect(screen.getByTestId(ADD_REQUIRED_FIELD_BUTTON_TEST_ID)).toBeDisabled(); + }); + + it('adding a new required field is disabled when an empty row is already displayed', async () => { + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string'] }), + ]; + + render(); + + expect(screen.getByTestId(ADD_REQUIRED_FIELD_BUTTON_TEST_ID)).toBeEnabled(); + + await addRequiredFieldRow(); + + expect(screen.getByTestId(ADD_REQUIRED_FIELD_BUTTON_TEST_ID)).toBeDisabled(); + }); + + describe('warnings', () => { + it('displays a warning when a selected field name is not found within index patterns', async () => { + const initialState = [{ name: 'field-that-does-not-exist', type: 'keyword' }]; + + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string'] }), + ]; + + render(); + + expect(screen.getByTestId(REQUIRED_FIELDS_GENERAL_WARNING_TEST_ID)).toBeVisible(); + + expect( + screen.getByText( + `Field "field-that-does-not-exist" is not found within the rule's specified index patterns` + ) + ).toBeVisible(); + + const nameWarningIcon = screen + .getByTestId(`requiredFieldNameSelect-field-that-does-not-exist`) + .querySelector('[data-euiicon-type="warning"]'); + + expect(nameWarningIcon).toBeVisible(); + + /* Make sure only one warning icon is displayed - the one for name */ + expect(document.querySelectorAll('[data-test-subj="warningIcon"]')).toHaveLength(1); + }); + + it('displays a warning when a selected field type is not found within index patterns', async () => { + const initialState = [{ name: 'field1', type: 'type-that-does-not-exist' }]; + + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string'] }), + ]; + + render(); + + expect(screen.getByTestId(REQUIRED_FIELDS_GENERAL_WARNING_TEST_ID)).toBeVisible(); + + expect( + screen.getByText( + `Field "field1" with type "type-that-does-not-exist" is not found within the rule's specified index patterns` + ) + ).toBeVisible(); + + const typeWarningIcon = screen + .getByTestId(`requiredFieldTypeSelect-type-that-does-not-exist`) + .querySelector('[data-euiicon-type="warning"]'); + + expect(typeWarningIcon).toBeVisible(); + + /* Make sure only one warning icon is displayed - the one for type */ + expect(document.querySelectorAll('[data-test-subj="warningIcon"]')).toHaveLength(1); + }); + + it('displays a warning only for field name when both field name and type are not found within index patterns', async () => { + const initialState = [ + { name: 'field-that-does-not-exist', type: 'type-that-does-not-exist' }, + ]; + + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string'] }), + ]; + + render(); + + expect(screen.getByTestId(REQUIRED_FIELDS_GENERAL_WARNING_TEST_ID)).toBeVisible(); + + expect( + screen.getByText( + `Field "field-that-does-not-exist" is not found within the rule's specified index patterns` + ) + ).toBeVisible(); + + const nameWarningIcon = screen + .getByTestId(`requiredFieldNameSelect-field-that-does-not-exist`) + .querySelector('[data-euiicon-type="warning"]'); + + expect(nameWarningIcon).toBeVisible(); + + /* Make sure only one warning icon is displayed - the one for name */ + expect(document.querySelectorAll('[data-test-subj="warningIcon"]')).toHaveLength(1); + }); + + it(`doesn't display a warning when all selected fields are found within index patterns`, async () => { + const initialState = [{ name: 'field1', type: 'string' }]; + + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string'] }), + ]; + + render(); + + expect(screen.queryByTestId(REQUIRED_FIELDS_GENERAL_WARNING_TEST_ID)).toBeNull(); + }); + + it(`doesn't display a warning for an empty row`, async () => { + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string'] }), + ]; + + render(); + + await addRequiredFieldRow(); + + expect(screen.queryByTestId(REQUIRED_FIELDS_GENERAL_WARNING_TEST_ID)).toBeNull(); + }); + + it(`doesn't display a warning when field is invalid`, async () => { + render(); + + await addRequiredFieldRow(); + + await typeInCustomComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForName('empty'), + optionText: 'customField', + }); + + expect(screen.getByText('Field type is required')).toBeVisible(); + + expect(screen.queryByTestId(`customField-warningText`)).toBeNull(); + }); + }); + + describe('validation', () => { + it('form is invalid when only field name is empty', async () => { + render(); + + await addRequiredFieldRow(); + + await typeInCustomComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForType('empty'), + optionText: 'customType', + }); + + expect(screen.getByText('Field name is required')).toBeVisible(); + + await typeInCustomComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForName('empty'), + optionText: 'customField', + }); + + expect(screen.queryByText('Field name is required')).toBeNull(); + }); + + it('form is invalid when only field type is empty', async () => { + render(); + + await addRequiredFieldRow(); + + await typeInCustomComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForName('empty'), + optionText: 'customField', + }); + + expect(screen.getByText('Field type is required')).toBeVisible(); + + await typeInCustomComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForType('empty'), + optionText: 'customType', + }); + + expect(screen.queryByText('Field type is required')).toBeNull(); + }); + + it('form is invalid when same field name is selected more than once', async () => { + const initialState = [{ name: 'field1', type: 'string' }]; + + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string'] }), + createIndexPatternField({ name: 'field2', esTypes: ['string'] }), + ]; + + render(); + + await addRequiredFieldRow(); + + await typeInCustomComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForName('empty'), + optionText: 'field1', + }); + + expect(screen.getByText('Field name "field1" is already used')).toBeVisible(); + + await typeInCustomComboBoxOption({ + comboBoxToggleButton: getLastSelectToggleButtonForName(), + optionText: 'field2', + }); + + expect(screen.queryByText('Field name "field1" is already used')).toBeNull(); + }); + + it('form is valid when both field name and type are empty', async () => { + const handleSubmit = jest.fn(); + + render(); + + await addRequiredFieldRow(); + + await submitForm(); + + expect(handleSubmit).toHaveBeenCalledWith({ + data: [{ name: '', type: '' }], + isValid: true, + }); + }); + }); + + describe('form submission', () => { + it('submits undefined when no required fields are selected', async () => { + const handleSubmit = jest.fn(); + + render(); + + await submitForm(); + + await waitFor(() => { + expect(handleSubmit).toHaveBeenCalledWith({ + data: undefined, + isValid: true, + }); + }); + }); + + it('submits undefined when all selected fields were removed', async () => { + const initialState = [{ name: 'field1', type: 'string' }]; + + const handleSubmit = jest.fn(); + + render(); + + await act(async () => { + fireEvent.click(screen.getByTestId('removeRequiredFieldButton-field1')); + }); + + await submitForm(); + + await waitFor(() => { + expect(handleSubmit).toHaveBeenCalledWith({ + data: undefined, + isValid: true, + }); + }); + }); + + it('submits newly added required fields', async () => { + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string'] }), + ]; + + const handleSubmit = jest.fn(); + + render(); + + await addRequiredFieldRow(); + + await selectFirstEuiComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForName('empty'), + }); + + await submitForm(); + + expect(handleSubmit).toHaveBeenCalledWith({ + data: [{ name: 'field1', type: 'string' }], + isValid: true, + }); + }); + + it('submits previously saved required fields', async () => { + const initialState = [{ name: 'field1', type: 'string' }]; + + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string'] }), + ]; + + const handleSubmit = jest.fn(); + + render( + + ); + + await submitForm(); + + expect(handleSubmit).toHaveBeenCalledWith({ + data: [{ name: 'field1', type: 'string' }], + isValid: true, + }); + }); + + it('submits updated required fields', async () => { + const initialState = [{ name: 'field1', type: 'string' }]; + + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string'] }), + createIndexPatternField({ name: 'field2', esTypes: ['keyword', 'date'] }), + ]; + + const handleSubmit = jest.fn(); + + render( + + ); + + await selectEuiComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForName('field1'), + optionText: 'field2', + }); + + await selectEuiComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForType('keyword'), + optionText: 'date', + }); + + await submitForm(); + + expect(handleSubmit).toHaveBeenCalledWith({ + data: [{ name: 'field2', type: 'date' }], + isValid: true, + }); + }); + + it('submits a form with warnings', async () => { + const initialState = [{ name: 'name-that-does-not-exist', type: 'type-that-does-not-exist' }]; + + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string'] }), + ]; + + const handleSubmit = jest.fn(); + + render( + + ); + + expect(screen.queryByTestId(REQUIRED_FIELDS_GENERAL_WARNING_TEST_ID)).toBeVisible(); + + await submitForm(); + + expect(handleSubmit).toHaveBeenCalledWith({ + data: [{ name: 'name-that-does-not-exist', type: 'type-that-does-not-exist' }], + isValid: true, + }); + }); + }); +}); + +export function createIndexPatternField(overrides: Partial): DataViewFieldBase { + return { + name: 'one', + type: 'string', + esTypes: [], + ...overrides, + }; +} + +async function getDropdownOptions(dropdownToggleButton: HTMLElement): Promise { + await showEuiComboBoxOptions(dropdownToggleButton); + + const options = screen.getAllByRole('option').map((option) => option.textContent) as string[]; + + fireEvent.click(dropdownToggleButton); + + return options; +} + +export function addRequiredFieldRow(): Promise { + return act(async () => { + fireEvent.click(screen.getByText('Add required field')); + }); +} + +function showEuiComboBoxOptions(comboBoxToggleButton: HTMLElement): Promise { + fireEvent.click(comboBoxToggleButton); + + return waitFor(() => { + const listWithOptionsElement = document.querySelector('[role="listbox"]'); + const emptyListElement = document.querySelector('.euiComboBoxOptionsList__empty'); + + expect(listWithOptionsElement || emptyListElement).toBeInTheDocument(); + }); +} + +type SelectEuiComboBoxOptionParameters = + | { + comboBoxToggleButton: HTMLElement; + optionIndex: number; + optionText?: undefined; + } + | { + comboBoxToggleButton: HTMLElement; + optionText: string; + optionIndex?: undefined; + }; + +function selectEuiComboBoxOption({ + comboBoxToggleButton, + optionIndex, + optionText, +}: SelectEuiComboBoxOptionParameters): Promise { + return act(async () => { + await showEuiComboBoxOptions(comboBoxToggleButton); + + const options = Array.from( + document.querySelectorAll('[data-test-subj*="comboBoxOptionsList"] [role="option"]') + ); + + if (typeof optionText === 'string') { + const optionToSelect = options.find((option) => option.textContent === optionText); + + if (!optionToSelect) { + throw new Error( + `Could not find option with text "${optionText}". Available options: ${options + .map((option) => option.textContent) + .join(', ')}` + ); + } + + fireEvent.click(optionToSelect); + } else { + fireEvent.click(options[optionIndex]); + } + }); +} + +function selectFirstEuiComboBoxOption({ + comboBoxToggleButton, +}: { + comboBoxToggleButton: HTMLElement; +}): Promise { + return selectEuiComboBoxOption({ comboBoxToggleButton, optionIndex: 0 }); +} + +function typeInCustomComboBoxOption({ + comboBoxToggleButton, + optionText, +}: { + comboBoxToggleButton: HTMLElement; + optionText: string; +}) { + return act(async () => { + await showEuiComboBoxOptions(comboBoxToggleButton); + + fireEvent.change(document.activeElement as HTMLInputElement, { target: { value: optionText } }); + fireEvent.keyDown(document.activeElement as HTMLInputElement, { key: 'Enter' }); + }); +} + +function getLastSelectToggleButtonForName(): HTMLElement { + const allNameSelects = screen.getAllByTestId(/requiredFieldNameSelect-.*/); + const lastNameSelect = allNameSelects[allNameSelects.length - 1]; + + return lastNameSelect.querySelector('[data-test-subj="comboBoxToggleListButton"]') as HTMLElement; +} + +export function getSelectToggleButtonForName(value: string): HTMLElement { + return screen + .getByTestId(`requiredFieldNameSelect-${value}`) + .querySelector('[data-test-subj="comboBoxToggleListButton"]') as HTMLElement; +} + +function getSelectToggleButtonForType(value: string): HTMLElement { + return screen + .getByTestId(`requiredFieldTypeSelect-${value}`) + .querySelector('[data-test-subj="comboBoxToggleListButton"]') as HTMLElement; +} + +function submitForm(): Promise { + return act(async () => { + fireEvent.click(screen.getByText('Submit')); + }); +} + +interface TestFormProps { + initialState?: RequiredFieldInput[]; + onSubmit?: (args: { data: RequiredFieldInput[]; isValid: boolean }) => void; + indexPatternFields?: DataViewFieldBase[]; + isIndexPatternLoading?: boolean; +} + +function TestForm({ + indexPatternFields, + initialState = [], + isIndexPatternLoading, + onSubmit, +}: TestFormProps): JSX.Element { + const { form } = useForm({ + defaultValue: { + requiredFieldsField: initialState, + }, + onSubmit: async (formData, isValid) => + onSubmit?.({ data: formData.requiredFieldsField, isValid }), + }); + + return ( + +
+ + + +
+ ); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields.tsx new file mode 100644 index 0000000000000..f53c41ce98d00 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields.tsx @@ -0,0 +1,213 @@ +/* + * Copyright 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, EuiCallOut, EuiFormRow, EuiSpacer, EuiText } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import type { DataViewFieldBase } from '@kbn/es-query'; +import type { RequiredFieldInput } from '../../../../../common/api/detection_engine'; +import { UseArray, useFormData } from '../../../../shared_imports'; +import type { FormHook, ArrayItem } from '../../../../shared_imports'; +import { RequiredFieldsHelpInfo } from './required_fields_help_info'; +import { RequiredFieldRow } from './required_fields_row'; +import * as defineRuleI18n from '../../../rule_creation_ui/components/step_define_rule/translations'; +import * as i18n from './translations'; + +interface RequiredFieldsComponentProps { + path: string; + indexPatternFields?: DataViewFieldBase[]; + isIndexPatternLoading?: boolean; +} + +const RequiredFieldsComponent = ({ + path, + indexPatternFields = [], + isIndexPatternLoading = false, +}: RequiredFieldsComponentProps) => { + return ( + + {({ items, addItem, removeItem, form }) => ( + + )} + + ); +}; + +interface RequiredFieldsListProps { + items: ArrayItem[]; + addItem: () => void; + removeItem: (id: number) => void; + indexPatternFields: DataViewFieldBase[]; + isIndexPatternLoading: boolean; + path: string; + form: FormHook; +} + +const RequiredFieldsList = ({ + items, + addItem, + removeItem, + indexPatternFields, + isIndexPatternLoading, + path, + form, +}: RequiredFieldsListProps) => { + /* + This component should only re-render when either the "index" form field (index patterns) or the required fields change. + + By default, the `useFormData` hook triggers a re-render whenever any form field changes. + It also allows optimization by passing a "watch" array of field names. The component then only re-renders when these specified fields change. + + However, it doesn't work with fields created using the `UseArray` component. + In `useFormData`, these array fields are stored as "flattened" objects with numbered keys, like { "requiredFields[0]": { ... }, "requiredFields[1]": { ... } }. + The "watch" feature of `useFormData` only works if you pass these "flattened" field names, such as ["requiredFields[0]", "requiredFields[1]", ...], not just "requiredFields". + + To work around this, we manually construct a list of "flattened" field names to watch, based on the current state of the form. + This is a temporary solution and ideally, `useFormData` should be updated to handle this scenario. + */ + + const internalField = form.getFields()[`${path}__array__`] ?? {}; + const internalFieldValue = (internalField?.value ?? []) as ArrayItem[]; + const flattenedFieldNames = internalFieldValue.map((item) => item.path); + + /* + Not using "watch" for the initial render, to let row components render and initialize form fields. + Then we can use the "watch" feature to track their changes. + */ + const hasRenderedInitially = flattenedFieldNames.length > 0; + const fieldsToWatch = hasRenderedInitially ? ['index', ...flattenedFieldNames] : []; + + const [formData] = useFormData({ watch: fieldsToWatch }); + + const fieldValue: RequiredFieldInput[] = formData[path] ?? []; + + const typesByFieldName: Record = useMemo( + () => + indexPatternFields.reduce((accumulator, field) => { + if (field.esTypes?.length) { + accumulator[field.name] = field.esTypes; + } + return accumulator; + }, {} as Record), + [indexPatternFields] + ); + + const allFieldNames = useMemo(() => Object.keys(typesByFieldName), [typesByFieldName]); + + const selectedFieldNames = fieldValue.map(({ name }) => name); + + const availableFieldNames = allFieldNames.filter((name) => !selectedFieldNames.includes(name)); + + const nameWarnings = fieldValue.reduce>((warnings, { name }) => { + if ( + !isIndexPatternLoading && + /* Creating a warning only if "name" value is filled in */ + name !== '' && + !allFieldNames.includes(name) + ) { + warnings[name] = i18n.FIELD_NAME_NOT_FOUND_WARNING(name); + } + return warnings; + }, {}); + + const typeWarnings = fieldValue.reduce>((warnings, { name, type }) => { + if ( + !isIndexPatternLoading && + /* Creating a warning for "type" only if "name" value is filled in */ + name !== '' && + typesByFieldName[name] && + !typesByFieldName[name].includes(type) + ) { + warnings[`${name}-${type}`] = i18n.FIELD_TYPE_NOT_FOUND_WARNING(name, type); + } + return warnings; + }, {}); + + const getWarnings = ({ name, type }: { name: string; type: string }) => ({ + nameWarning: nameWarnings[name] || '', + typeWarning: typeWarnings[`${name}-${type}`] || '', + }); + + const hasEmptyFieldName = fieldValue.some(({ name }) => name === ''); + + const hasWarnings = Object.keys(nameWarnings).length > 0 || Object.keys(typeWarnings).length > 0; + + return ( + <> + {hasWarnings && ( + +

+ {defineRuleI18n.SOURCE}, + }} + /> +

+
+ )} + + + {i18n.REQUIRED_FIELDS_LABEL} + + + } + labelAppend={ + + {i18n.OPTIONAL} + + } + hasChildLabel={false} + labelType="legend" + > + <> + {items.map((item) => ( + + ))} + + + + {i18n.ADD_REQUIRED_FIELD} + + + + + ); +}; + +export const RequiredFields = React.memo(RequiredFieldsComponent); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields_help_info.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields_help_info.tsx new file mode 100644 index 0000000000000..187f05880d205 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields_help_info.tsx @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { useToggle } from 'react-use'; +import { EuiPopover, EuiText, EuiButtonIcon } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import * as defineRuleI18n from '../../../rule_creation_ui/components/step_define_rule/translations'; +import * as i18n from './translations'; + +/** + * Theme doesn't expose width variables. Using provided size variables will require + * multiplying it by another magic constant. + * + * 320px width looks + * like a [commonly used width in EUI](https://github.com/search?q=repo%3Aelastic%2Feui%20320&type=code). + */ +const POPOVER_WIDTH = 320; + +export function RequiredFieldsHelpInfo(): JSX.Element { + const [isPopoverOpen, togglePopover] = useToggle(false); + + const button = ( + + ); + + return ( + + + {defineRuleI18n.SOURCE}, + }} + /> + + + ); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields_row.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields_row.tsx new file mode 100644 index 0000000000000..755f1de413760 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields_row.tsx @@ -0,0 +1,163 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback, useMemo } from 'react'; +import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiTextColor } from '@elastic/eui'; +import { UseField } from '../../../../shared_imports'; +import { NameComboBox } from './name_combobox'; +import { TypeComboBox } from './type_combobox'; +import { makeValidateRequiredField } from './make_validate_required_field'; +import * as i18n from './translations'; + +import type { ArrayItem, FieldConfig, FieldHook } from '../../../../shared_imports'; +import type { + RequiredField, + RequiredFieldInput, +} from '../../../../../common/api/detection_engine/model/rule_schema/common_attributes.gen'; + +interface RequiredFieldRowProps { + item: ArrayItem; + removeItem: (id: number) => void; + typesByFieldName: Record; + availableFieldNames: string[]; + getWarnings: ({ name, type }: { name: string; type: string }) => { + nameWarning: string; + typeWarning: string; + }; + parentFieldPath: string; +} + +export const RequiredFieldRow = ({ + item, + removeItem, + typesByFieldName, + availableFieldNames, + getWarnings, + parentFieldPath, +}: RequiredFieldRowProps) => { + const handleRemove = useCallback(() => removeItem(item.id), [removeItem, item.id]); + + const rowFieldConfig: FieldConfig = + useMemo( + () => ({ + deserializer: (value) => { + const rowValueWithoutEcs: RequiredFieldInput = { + name: value.name, + type: value.type, + }; + + return rowValueWithoutEcs; + }, + validations: [{ validator: makeValidateRequiredField(parentFieldPath) }], + defaultValue: { name: '', type: '' }, + }), + [parentFieldPath] + ); + + return ( + + ); +}; + +interface RequiredFieldFieldProps { + field: FieldHook; + onRemove: () => void; + typesByFieldName: Record; + availableFieldNames: string[]; + getWarnings: ({ name, type }: { name: string; type: string }) => { + nameWarning: string; + typeWarning: string; + }; + itemId: string; +} + +const RequiredFieldField = ({ + field, + typesByFieldName, + onRemove, + availableFieldNames, + getWarnings, + itemId, +}: RequiredFieldFieldProps) => { + const { nameWarning, typeWarning } = getWarnings(field.value); + const warningMessage = nameWarning || typeWarning; + + const [nameError, typeError] = useMemo(() => { + return [ + field.errors.find((error) => 'path' in error && error.path === `${field.path}.name`), + field.errors.find((error) => 'path' in error && error.path === `${field.path}.type`), + ]; + }, [field.path, field.errors]); + const hasError = Boolean(nameError) || Boolean(typeError); + const errorMessage = nameError?.message || typeError?.message; + + return ( + + {warningMessage} + + ) : ( + '' + ) + } + color="warning" + > + + + + + + + + + + + + + ); +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/translations.ts new file mode 100644 index 0000000000000..bed9a4ea0024c --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/translations.ts @@ -0,0 +1,105 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const REQUIRED_FIELDS_LABEL = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDescription.requiredFields.requiredFieldsLabel', + { + defaultMessage: 'Required fields', + } +); + +export const FIELD_NAME = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDescription.requiredFields.fieldNameLabel', + { + defaultMessage: 'Field name', + } +); + +export const FIELD_TYPE = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDescription.requiredFields.fieldTypeLabel', + { + defaultMessage: 'Field type', + } +); + +export const OPEN_HELP_POPOVER_ARIA_LABEL = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDescription.requiredFields.openHelpPopoverAriaLabel', + { + defaultMessage: 'Open help popover', + } +); + +export const REQUIRED_FIELDS_GENERAL_WARNING_TITLE = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDescription.requiredFields.generalWarningTitle', + { + defaultMessage: `Some fields aren't found within the rule's specified index patterns.`, + } +); + +export const OPTIONAL = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDescription.requiredFields.optionalText', + { + defaultMessage: 'Optional', + } +); + +export const REMOVE_REQUIRED_FIELD_BUTTON_ARIA_LABEL = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDescription.requiredFields.removeRequiredFieldButtonAriaLabel', + { + defaultMessage: 'Remove required field', + } +); + +export const ADD_REQUIRED_FIELD = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDescription.requiredFields.addRequiredFieldButtonLabel', + { + defaultMessage: 'Add required field', + } +); + +export const FIELD_NAME_NOT_FOUND_WARNING = (name: string) => + i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDescription.requiredFields.fieldNameNotFoundWarning', + { + values: { name }, + defaultMessage: `Field "{name}" is not found within the rule's specified index patterns`, + } + ); + +export const FIELD_TYPE_NOT_FOUND_WARNING = (name: string, type: string) => + i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDescription.requiredFields.fieldTypeNotFoundWarning', + { + values: { name, type }, + defaultMessage: `Field "{name}" with type "{type}" is not found within the rule's specified index patterns`, + } + ); + +export const FIELD_NAME_REQUIRED = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDescription.requiredFields.validation.fieldNameRequired', + { + defaultMessage: 'Field name is required', + } +); + +export const FIELD_TYPE_REQUIRED = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDescription.requiredFields.validation.fieldTypeRequired', + { + defaultMessage: 'Field type is required', + } +); + +export const FIELD_NAME_USED_MORE_THAN_ONCE = (name: string) => + i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDescription.requiredFields.validation.fieldNameUsedMoreThanOnce', + { + values: { name }, + defaultMessage: 'Field name "{name}" is already used', + } + ); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/type_combobox.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/type_combobox.tsx new file mode 100644 index 0000000000000..48d5a009c4abe --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/type_combobox.tsx @@ -0,0 +1,151 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback, useMemo, useState, useEffect } from 'react'; +import { EuiComboBox, EuiIcon } from '@elastic/eui'; +import type { EuiComboBoxOptionOption } from '@elastic/eui'; +import { euiThemeVars } from '@kbn/ui-theme'; +import type { FieldHook } from '../../../../shared_imports'; +import type { RequiredFieldInput } from '../../../../../common/api/detection_engine/model/rule_schema/common_attributes.gen'; +import * as i18n from './translations'; + +interface TypeComboBoxProps { + field: FieldHook; + itemId: string; + typesByFieldName: Record; + typeWarning: string; + typeError: { message: string } | undefined; +} + +export function TypeComboBox({ + field, + itemId, + typesByFieldName, + typeWarning, + typeError, +}: TypeComboBoxProps) { + const { value, setValue } = field; + + const selectableTypeOptions: Array> = useMemo(() => { + const typesAvailableForSelectedName = typesByFieldName[value.name]; + const isSelectedTypeAvailable = (typesAvailableForSelectedName || []).includes(value.type); + + if (typesAvailableForSelectedName && isSelectedTypeAvailable) { + /* + Case: name is available, type is not available + Selected field name is present in index patterns, so it has one or more types available for it. + Allowing the user to select from them. + */ + + return typesAvailableForSelectedName.map((type) => ({ + label: type, + value: type, + })); + } else if (typesAvailableForSelectedName) { + /* + Case: name is available, type is not available + Selected field name is present in index patterns, but the selected type doesn't exist for it. + Adding the selected type to the list of selectable options since it was selected before. + */ + return typesAvailableForSelectedName + .map((type) => ({ + label: type, + value: type, + })) + .concat({ label: value.type, value: value.type }); + } else if (value.name) { + /* + Case: name is not available (so the type is also not available) + Field name is set (not an empty string), but it's not present in index patterns. + In such case the only selectable type option is the currenty selected type. + */ + return [ + { + label: value.type, + value: value.type, + }, + ]; + } + + return []; + }, [value.name, value.type, typesByFieldName]); + + /* + Using a state for `selectedTypeOptions` instead of using the field value directly + to fix the issue where pressing the backspace key in combobox input would clear the field value + and trigger a validation error. By using a separate state, we can clear the selected option + without clearing the field value. + */ + const [selectedTypeOption, setSelectedTypeOption] = useState< + EuiComboBoxOptionOption | undefined + >(selectableTypeOptions.find((option) => option.value === value.type)); + + useEffect(() => { + /* Re-computing the new selected type option when the field value changes */ + setSelectedTypeOption(selectableTypeOptions.find((option) => option.value === value.type)); + }, [value.type, selectableTypeOptions]); + + const handleTypeChange = useCallback( + (selectedOptions: Array>) => { + const newlySelectedOption: EuiComboBoxOptionOption | undefined = selectedOptions[0]; + + if (!newlySelectedOption) { + /* This occurs when the user hits backspace in combobox */ + setSelectedTypeOption(undefined); + return; + } + + const updatedType = newlySelectedOption?.value || ''; + + const updatedFieldValue: RequiredFieldInput = { + name: value.name, + type: updatedType, + }; + + setValue(updatedFieldValue); + }, + [value.name, setValue] + ); + + const handleAddCustomType = useCallback( + (newType: string) => { + const updatedFieldValue: RequiredFieldInput = { + name: value.name, + type: newType, + }; + + setValue(updatedFieldValue); + }, + [value.name, setValue] + ); + + return ( + + ) : undefined + } + /> + ); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/utils.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/utils.test.tsx new file mode 100644 index 0000000000000..235da2208a43b --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/utils.test.tsx @@ -0,0 +1,58 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { pickTypeForName } from './utils'; + +describe('pickTypeForName', () => { + it('returns the current type if it is available for the current name', () => { + const typesByFieldName = { + name1: ['text', 'keyword'], + }; + + expect( + pickTypeForName({ + name: 'name1', + type: 'keyword', + typesByFieldName, + }) + ).toEqual('keyword'); + }); + + it('returns the first available type if the current type is not available for the current name', () => { + const typesByFieldName = { + name1: ['text', 'keyword'], + }; + + expect( + pickTypeForName({ + name: 'name1', + type: 'long', + typesByFieldName, + }) + ).toEqual('text'); + }); + + it('returns the current type if no types are available for the current name', () => { + expect( + pickTypeForName({ + name: 'name1', + type: 'keyword', + typesByFieldName: {}, + }) + ).toEqual('keyword'); + + expect( + pickTypeForName({ + name: 'name1', + type: 'keyword', + typesByFieldName: { + name1: [], + }, + }) + ).toEqual('keyword'); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/utils.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/utils.ts new file mode 100644 index 0000000000000..55beca264e120 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/utils.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +interface PickTypeForNameParameters { + name: string; + type: string; + typesByFieldName?: Record; +} + +export function pickTypeForName({ name, type, typesByFieldName = {} }: PickTypeForNameParameters) { + const typesAvailableForName = typesByFieldName[name] || []; + const isCurrentTypeAvailableForNewName = typesAvailableForName.includes(type); + + /* First try to keep the type if it's available for the name */ + if (isCurrentTypeAvailableForNewName) { + return type; + } + + /* + If current type is not available, pick the first available type. + If no type is available, use the current type. + */ + return typesAvailableForName[0] ?? type; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/logic/esql_validator.test.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/logic/esql_validator.test.ts index 889d74a1c6503..07f14830d6a71 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/logic/esql_validator.test.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/logic/esql_validator.test.ts @@ -5,7 +5,13 @@ * 2.0. */ -import { computeHasMetadataOperator } from './esql_validator'; +import { parseEsqlQuery, computeHasMetadataOperator } from './esql_validator'; + +import { computeIsESQLQueryAggregating } from '@kbn/securitysolution-utils'; + +jest.mock('@kbn/securitysolution-utils', () => ({ computeIsESQLQueryAggregating: jest.fn() })); + +const computeIsESQLQueryAggregatingMock = computeIsESQLQueryAggregating as jest.Mock; describe('computeHasMetadataOperator', () => { it('should be false if query does not have operator', () => { @@ -44,3 +50,37 @@ describe('computeHasMetadataOperator', () => { ).toBe(true); }); }); + +describe('parseEsqlQuery', () => { + it('returns isMissingMetadataOperator true when query is not aggregating and does not have metadata operator', () => { + computeIsESQLQueryAggregatingMock.mockReturnValueOnce(false); + + expect(parseEsqlQuery('from test*')).toEqual({ + isEsqlQueryAggregating: false, + isMissingMetadataOperator: true, + }); + }); + + it('returns isMissingMetadataOperator false when query is not aggregating and has metadata operator', () => { + computeIsESQLQueryAggregatingMock.mockReturnValueOnce(false); + + expect(parseEsqlQuery('from test* metadata _id')).toEqual({ + isEsqlQueryAggregating: false, + isMissingMetadataOperator: false, + }); + }); + + it('returns isMissingMetadataOperator false when query is aggregating', () => { + computeIsESQLQueryAggregatingMock.mockReturnValue(true); + + expect(parseEsqlQuery('from test*')).toEqual({ + isEsqlQueryAggregating: true, + isMissingMetadataOperator: false, + }); + + expect(parseEsqlQuery('from test* metadata _id')).toEqual({ + isEsqlQueryAggregating: true, + isMissingMetadataOperator: false, + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/logic/esql_validator.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/logic/esql_validator.ts index b7e9f1b033e31..1f0bcb6596b40 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/logic/esql_validator.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/logic/esql_validator.ts @@ -6,11 +6,10 @@ */ import { isEmpty } from 'lodash'; - +import type { QueryClient } from '@tanstack/react-query'; import { computeIsESQLQueryAggregating } from '@kbn/securitysolution-utils'; import { KibanaServices } from '../../../common/lib/kibana'; -import { securitySolutionQueryClient } from '../../../common/containers/query_client/query_client_provider'; import type { ValidationError, ValidationFunc } from '../../../shared_imports'; import { isEsqlRule } from '../../../../common/detection_engine/utils'; @@ -48,7 +47,7 @@ export const computeHasMetadataOperator = (esqlQuery: string) => { export const esqlValidator = async ( ...args: Parameters ): Promise | void | undefined> => { - const [{ value, formData }] = args; + const [{ value, formData, customData }] = args; const { query: queryValue } = value as FieldValueQueryBar; const query = queryValue.query as string; const { ruleType } = formData as DefineStepRule; @@ -59,19 +58,19 @@ export const esqlValidator = async ( } try { - const services = KibanaServices.get(); + const queryClient = (customData.value as { queryClient: QueryClient | undefined })?.queryClient; - const isEsqlQueryAggregating = computeIsESQLQueryAggregating(query); + const services = KibanaServices.get(); + const { isEsqlQueryAggregating, isMissingMetadataOperator } = parseEsqlQuery(query); - // non-aggregating query which does not have metadata, is not a valid one - if (!isEsqlQueryAggregating && !computeHasMetadataOperator(query)) { + if (isMissingMetadataOperator) { return { code: ERROR_CODES.ERR_MISSING_ID_FIELD_FROM_RESULT, message: i18n.ESQL_VALIDATION_MISSING_ID_IN_QUERY_ERROR, }; } - const columns = await securitySolutionQueryClient.fetchQuery( + const columns = await queryClient?.fetchQuery( getEsqlQueryConfig({ esqlQuery: query, search: services.data.search.search }) ); @@ -92,3 +91,17 @@ export const esqlValidator = async ( return constructValidationError(error); } }; + +/** + * check if esql query valid for Security rule: + * - if it's non aggregation query it must have metadata operator + */ +export const parseEsqlQuery = (query: string) => { + const isEsqlQueryAggregating = computeIsESQLQueryAggregating(query); + + return { + isEsqlQueryAggregating, + // non-aggregating query which does not have [metadata], is not a valid one + isMissingMetadataOperator: !isEsqlQueryAggregating && !computeHasMetadataOperator(query), + }; +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/helpers.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/helpers.tsx index aaf588edd3099..8ef2a3751a036 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/helpers.tsx @@ -18,12 +18,9 @@ import { } from '@elastic/eui'; import { ALERT_RISK_SCORE } from '@kbn/rule-data-utils'; -import { castEsToKbnFieldTypeName } from '@kbn/field-types'; - import { isEmpty } from 'lodash/fp'; import React from 'react'; import styled from 'styled-components'; -import { FieldIcon } from '@kbn/react-field'; import type { ThreatMapping, Type, Threats } from '@kbn/securitysolution-io-ts-alerting-types'; import { FilterBadgeGroup } from '@kbn/unified-search-plugin/public'; @@ -50,8 +47,10 @@ import type { } from '../../../../detections/pages/detection_engine/rules/types'; import { GroupByOptions } from '../../../../detections/pages/detection_engine/rules/types'; import { defaultToEmptyTag } from '../../../../common/components/empty_value'; +import { RequiredFieldIcon } from '../../../rule_management/components/rule_details/required_field_icon'; import { ThreatEuiFlexGroup } from './threat_description'; import { AlertSuppressionLabel } from './alert_suppression_label'; + const NoteDescriptionContainer = styled(EuiFlexItem)` height: 105px; overflow-y: hidden; @@ -566,11 +565,7 @@ export const buildRequiredFieldsDescription = ( - + diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/index.test.tsx index 7950493a1b989..8695041697120 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/index.test.tsx @@ -575,7 +575,7 @@ describe('description_step', () => { }); describe('alert suppression', () => { - const ruleTypesWithoutSuppression: Type[] = ['esql', 'machine_learning']; + const ruleTypesWithoutSuppression: Type[] = ['machine_learning']; const suppressionFields = { groupByDuration: { unit: 'm', diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/index.tsx index 3fa1852e6aa05..7666a9ba8aee3 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/index.tsx @@ -42,7 +42,7 @@ import { useKibana } from '../../../../common/lib/kibana'; import { useRuleIndices } from '../../../rule_management/logic/use_rule_indices'; import { EsqlAutocomplete } from '../esql_autocomplete'; import { MultiSelectFieldsAutocomplete } from '../multi_select_fields'; -import { useInvestigationFields } from '../../hooks/use_investigation_fields'; +import { useAllEsqlRuleFields } from '../../hooks'; import { MaxSignals } from '../max_signals'; const CommonUseField = getUseField({ component: Field }); @@ -133,10 +133,11 @@ const StepAboutRuleComponent: FC = ({ [getFields] ); - const { investigationFields, isLoading: isInvestigationFieldsLoading } = useInvestigationFields({ - esqlQuery: isEsqlRuleValue ? esqlQuery : undefined, - indexPatternsFields: indexPattern.fields, - }); + const { fields: investigationFields, isLoading: isInvestigationFieldsLoading } = + useAllEsqlRuleFields({ + esqlQuery: isEsqlRuleValue ? esqlQuery : undefined, + indexPatternsFields: indexPattern.fields, + }); return ( <> diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.test.tsx index de34718ef050f..be1306d706357 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.test.tsx @@ -8,6 +8,7 @@ import React, { useEffect, useState } from 'react'; import { screen, fireEvent, render, within, act, waitFor } from '@testing-library/react'; import type { Type as RuleType } from '@kbn/securitysolution-io-ts-alerting-types'; +import type { DataViewBase } from '@kbn/es-query'; import { StepDefineRule, aggregatableFields } from '.'; import { mockBrowserFields } from '../../../../common/containers/source/mock'; import { useRuleFromTimeline } from '../../../../detections/containers/detection_engine/rules/use_rule_from_timeline'; @@ -18,6 +19,11 @@ import type { FormSubmitHandler } from '../../../../shared_imports'; import { useForm } from '../../../../shared_imports'; import type { DefineStepRule } from '../../../../detections/pages/detection_engine/rules/types'; import { fleetIntegrationsApi } from '../../../fleet_integrations/api/__mocks__'; +import { + addRequiredFieldRow, + createIndexPatternField, + getSelectToggleButtonForName, +} from '../../../rule_creation/components/required_fields/required_fields.test'; // Mocks integrations jest.mock('../../../fleet_integrations/api'); @@ -410,6 +416,116 @@ describe('StepDefineRule', () => { }); }); + describe('required fields', () => { + it('submits a form without selected required fields', async () => { + const initialState = { + index: ['test-index'], + queryBar: { + query: { query: '*:*', language: 'kuery' }, + filters: [], + saved_id: null, + }, + }; + const handleSubmit = jest.fn(); + + render(, { + wrapper: TestProviders, + }); + + await submitForm(); + + await waitFor(() => { + expect(handleSubmit).toHaveBeenCalled(); + }); + + expect(handleSubmit).toHaveBeenCalledWith( + expect.not.objectContaining({ + requiredFields: expect.anything(), + }), + true + ); + }); + + it('submits saved early required fields without the "ecs" property', async () => { + const initialState = { + index: ['test-index'], + queryBar: { + query: { query: '*:*', language: 'kuery' }, + filters: [], + saved_id: null, + }, + requiredFields: [{ name: 'host.name', type: 'string', ecs: false }], + }; + + const handleSubmit = jest.fn(); + + render(, { + wrapper: TestProviders, + }); + + await submitForm(); + + await waitFor(() => { + expect(handleSubmit).toHaveBeenCalled(); + }); + + expect(handleSubmit).toHaveBeenCalledWith( + expect.objectContaining({ + requiredFields: [{ name: 'host.name', type: 'string' }], + }), + true + ); + }); + + it('submits newly added required fields', async () => { + const initialState = { + index: ['test-index'], + queryBar: { + query: { query: '*:*', language: 'kuery' }, + filters: [], + saved_id: null, + }, + }; + + const indexPattern: DataViewBase = { + fields: [createIndexPatternField({ name: 'host.name', esTypes: ['string'] })], + title: '', + }; + + const handleSubmit = jest.fn(); + + render( + , + { + wrapper: TestProviders, + } + ); + + await addRequiredFieldRow(); + + await selectFirstEuiComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForName('empty'), + }); + + await submitForm(); + + await waitFor(() => { + expect(handleSubmit).toHaveBeenCalled(); + }); + + expect(handleSubmit).toHaveBeenCalledWith( + expect.objectContaining({ + requiredFields: [{ name: 'host.name', type: 'string' }], + }), + true + ); + }); + }); + describe('handleSetRuleFromTimeline', () => { it('updates KQL query correctly', () => { const kqlQuery = { @@ -497,14 +613,16 @@ describe('StepDefineRule', () => { }); interface TestFormProps { - ruleType?: RuleType; initialState?: Partial; + ruleType?: RuleType; + indexPattern?: DataViewBase; onSubmit?: FormSubmitHandler; } function TestForm({ - ruleType = stepDefineDefaultValue.ruleType, initialState, + ruleType = stepDefineDefaultValue.ruleType, + indexPattern = { fields: [], title: '' }, onSubmit, }: TestFormProps): JSX.Element { const [selectedEqlOptions, setSelectedEqlOptions] = useState(stepDefineDefaultValue.eqlOptions); @@ -524,7 +642,7 @@ function TestForm({ threatIndicesConfig={[]} optionsSelected={selectedEqlOptions} setOptionsSelected={setSelectedEqlOptions} - indexPattern={{ fields: [], title: '' }} + indexPattern={indexPattern} isIndexPatternLoading={false} browserFields={{}} isQueryBarValid={true} @@ -560,32 +678,71 @@ function addRelatedIntegrationRow(): Promise { }); } +function setVersion({ input, value }: { input: HTMLInputElement; value: string }): Promise { + return act(async () => { + fireEvent.input(input, { + target: { value }, + }); + }); +} + function showEuiComboBoxOptions(comboBoxToggleButton: HTMLElement): Promise { fireEvent.click(comboBoxToggleButton); return waitFor(() => { - expect(screen.getByRole('listbox')).toBeInTheDocument(); + const listWithOptionsElement = document.querySelector('[role="listbox"]'); + const emptyListElement = document.querySelector('.euiComboBoxOptionsList__empty'); + + expect(listWithOptionsElement || emptyListElement).toBeInTheDocument(); }); } +type SelectEuiComboBoxOptionParameters = + | { + comboBoxToggleButton: HTMLElement; + optionIndex: number; + optionText?: undefined; + } + | { + comboBoxToggleButton: HTMLElement; + optionText: string; + optionIndex?: undefined; + }; + function selectEuiComboBoxOption({ comboBoxToggleButton, optionIndex, -}: { - comboBoxToggleButton: HTMLElement; - optionIndex: number; -}): Promise { + optionText, +}: SelectEuiComboBoxOptionParameters): Promise { return act(async () => { await showEuiComboBoxOptions(comboBoxToggleButton); - fireEvent.click(within(screen.getByRole('listbox')).getAllByRole('option')[optionIndex]); + const options = Array.from( + document.querySelectorAll('[data-test-subj*="comboBoxOptionsList"] [role="option"]') + ); + + if (typeof optionText === 'string') { + const optionToSelect = options.find((option) => option.textContent === optionText); + + if (optionToSelect) { + fireEvent.click(optionToSelect); + } else { + throw new Error( + `Could not find option with text "${optionText}". Available options: ${options + .map((option) => option.textContent) + .join(', ')}` + ); + } + } else { + fireEvent.click(options[optionIndex]); + } }); } -function setVersion({ input, value }: { input: HTMLInputElement; value: string }): Promise { - return act(async () => { - fireEvent.input(input, { - target: { value }, - }); - }); +function selectFirstEuiComboBoxOption({ + comboBoxToggleButton, +}: { + comboBoxToggleButton: HTMLElement; +}): Promise { + return selectEuiComboBoxOption({ comboBoxToggleButton, optionIndex: 0 }); } diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.tsx index f0dcadc056315..839454922a14a 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.tsx @@ -28,6 +28,7 @@ import type { FieldSpec } from '@kbn/data-views-plugin/common'; import usePrevious from 'react-use/lib/usePrevious'; import type { BrowserFields } from '@kbn/timelines-plugin/common'; import type { Type } from '@kbn/securitysolution-io-ts-alerting-types'; +import { useQueryClient } from '@tanstack/react-query'; import type { SavedQuery } from '@kbn/data-plugin/public'; import type { DataViewBase } from '@kbn/es-query'; @@ -90,6 +91,7 @@ import type { BrowserField } from '../../../../common/containers/source'; import { useFetchIndex } from '../../../../common/containers/source'; import { NewTermsFields } from '../new_terms_fields'; import { ScheduleItem } from '../../../rule_creation/components/schedule_item_form'; +import { RequiredFields } from '../../../rule_creation/components/required_fields'; import { DocLink } from '../../../../common/components/links_to_docs/doc_link'; import { defaultCustomQuery } from '../../../../detections/pages/detection_engine/rules/utils'; import { MultiSelectFieldsAutocomplete } from '../multi_select_fields'; @@ -98,6 +100,7 @@ import { AlertSuppressionMissingFieldsStrategyEnum } from '../../../../../common import { DurationInput } from '../duration_input'; import { MINIMUM_LICENSE_FOR_SUPPRESSION } from '../../../../../common/detection_engine/constants'; import { useUpsellingMessage } from '../../../../common/hooks/use_upselling'; +import { useAllEsqlRuleFields } from '../../hooks'; import { useAlertSuppression } from '../../../rule_management/logic/use_alert_suppression'; import { RelatedIntegrations } from '../../../rule_creation/components/related_integrations'; @@ -190,6 +193,8 @@ const StepDefineRuleComponent: FC = ({ thresholdFields, enableThresholdSuppression, }) => { + const queryClient = useQueryClient(); + const { isSuppressionEnabled: isAlertSuppressionEnabled } = useAlertSuppression(ruleType); const mlCapabilities = useMlCapabilities(); const [openTimelineSearch, setOpenTimelineSearch] = useState(false); @@ -452,6 +457,13 @@ const StepDefineRuleComponent: FC = ({ ); const [{ queryBar }] = useFormData({ form, watch: ['queryBar'] }); + + const { fields: esqlSuppressionFields, isLoading: isEsqlSuppressionLoading } = + useAllEsqlRuleFields({ + esqlQuery: isEsqlRule(ruleType) ? (queryBar?.query?.query as string) : undefined, + indexPatternsFields: indexPattern.fields, + }); + const areSuppressionFieldsDisabledBySequence = isEqlRule(ruleType) && isEqlSequenceQuery(queryBar?.query?.query as string) && @@ -744,6 +756,7 @@ const StepDefineRuleComponent: FC = ({ path="queryBar" config={esqlQueryBarConfig} component={QueryBarDefineRule} + validationData={{ queryClient }} componentProps={{ ...queryBarProps, dataTestSubj: 'detectionEngineStepDefineRuleEsqlQueryBar', @@ -751,7 +764,7 @@ const StepDefineRuleComponent: FC = ({ }} /> ), - [queryBarProps, esqlQueryBarConfig] + [queryBarProps, esqlQueryBarConfig, queryClient] ); const QueryBarMemo = useMemo( @@ -915,7 +928,6 @@ const StepDefineRuleComponent: FC = ({ )} - {isQueryRule(ruleType) && ( <> @@ -940,7 +952,6 @@ const StepDefineRuleComponent: FC = ({ )} - <> = ({ /> - <> @@ -1062,9 +1072,13 @@ const StepDefineRuleComponent: FC = ({ path="groupByFields" component={MultiSelectFieldsAutocomplete} componentProps={{ - browserFields: termsAggregationFields, + browserFields: isEsqlRule(ruleType) + ? esqlSuppressionFields + : termsAggregationFields, isDisabled: - !isAlertSuppressionLicenseValid || areSuppressionFieldsDisabledBySequence, + !isAlertSuppressionLicenseValid || + areSuppressionFieldsDisabledBySequence || + isEsqlSuppressionLoading, disabledText: areSuppressionFieldsDisabledBySequence ? i18n.EQL_SEQUENCE_SUPPRESSION_DISABLE_TOOLTIP : alertSuppressionUpsellingMessage, @@ -1116,6 +1130,19 @@ const StepDefineRuleComponent: FC = ({ + + + {!isMlRule(ruleType) && ( + <> + + + + )} + = { }, relatedIntegrations: { type: FIELD_TYPES.JSON, + label: i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepAboutRule.fieldRelatedIntegrationsLabel', + { + defaultMessage: 'Related integrations', + } + ), }, requiredFields: { label: i18n.translate( diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/use_experimental_feature_fields_transform.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/use_experimental_feature_fields_transform.ts index c035fef5af6e4..c92c35688dd3b 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/use_experimental_feature_fields_transform.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/use_experimental_feature_fields_transform.ts @@ -7,6 +7,8 @@ import { useCallback } from 'react'; import type { DefineStepRule } from '../../../../detections/pages/detection_engine/rules/types'; +import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; +import { isEsqlRule } from '../../../../../common/detection_engine/utils'; /** * transforms DefineStepRule fields according to experimental feature flags @@ -14,9 +16,30 @@ import type { DefineStepRule } from '../../../../detections/pages/detection_engi export const useExperimentalFeatureFieldsTransform = >(): (( fields: T ) => T) => { - const transformer = useCallback((fields: T) => { - return fields; - }, []); + const isAlertSuppressionForEsqlRuleEnabled = useIsExperimentalFeatureEnabled( + 'alertSuppressionForEsqlRuleEnabled' + ); + + const transformer = useCallback( + (fields: T) => { + const isSuppressionDisabled = + isEsqlRule(fields.ruleType) && !isAlertSuppressionForEsqlRuleEnabled; + + // reset any alert suppression values hidden behind feature flag + if (isSuppressionDisabled) { + return { + ...fields, + groupByFields: [], + groupByRadioSelection: undefined, + groupByDuration: undefined, + suppressionMissingFields: undefined, + }; + } + + return fields; + }, + [isAlertSuppressionForEsqlRuleEnabled] + ); return transformer; }; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/index.tsx index d56c317b93fb1..ea248587365aa 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/index.tsx @@ -7,3 +7,4 @@ export { useEsqlIndex } from './use_esql_index'; export { useEsqlQueryForAboutStep } from './use_esql_query_for_about_step'; +export { useAllEsqlRuleFields } from './use_all_esql_rule_fields'; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_investigation_fields.test.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_all_esql_rule_fields.test.ts similarity index 50% rename from x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_investigation_fields.test.ts rename to x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_all_esql_rule_fields.test.ts index 597d44f47f0d9..996b3ca044864 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_investigation_fields.test.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_all_esql_rule_fields.test.ts @@ -7,16 +7,15 @@ import { renderHook } from '@testing-library/react-hooks'; import type { DataViewFieldBase } from '@kbn/es-query'; +import { getESQLQueryColumns } from '@kbn/esql-utils'; -import { useInvestigationFields } from './use_investigation_fields'; +import { useAllEsqlRuleFields } from './use_all_esql_rule_fields'; import { createQueryWrapperMock } from '../../../common/__mocks__/query_wrapper'; +import { parseEsqlQuery } from '../../rule_creation/logic/esql_validator'; -import { computeIsESQLQueryAggregating } from '@kbn/securitysolution-utils'; -import { getESQLQueryColumns } from '@kbn/esql-utils'; - -jest.mock('@kbn/securitysolution-utils', () => ({ - computeIsESQLQueryAggregating: jest.fn(), +jest.mock('../../rule_creation/logic/esql_validator', () => ({ + parseEsqlQuery: jest.fn(), })); jest.mock('@kbn/esql-utils', () => { @@ -26,7 +25,7 @@ jest.mock('@kbn/esql-utils', () => { }; }); -const computeIsESQLQueryAggregatingMock = computeIsESQLQueryAggregating as jest.Mock; +const parseEsqlQueryMock = parseEsqlQuery as jest.Mock; const getESQLQueryColumnsMock = getESQLQueryColumns as jest.Mock; const { wrapper } = createQueryWrapperMock(); @@ -48,16 +47,26 @@ const mockEsqlDatatable = { columns: [{ id: '_custom_field', name: '_custom_field', meta: { type: 'string' } }], }; -describe('useInvestigationFields', () => { +describe('useAllEsqlRuleFields', () => { beforeEach(() => { jest.clearAllMocks(); - getESQLQueryColumnsMock.mockResolvedValue(mockEsqlDatatable.columns); + getESQLQueryColumnsMock.mockImplementation(({ esqlQuery }) => + Promise.resolve( + esqlQuery === 'deduplicate_test' + ? [ + { id: 'agent.name', name: 'agent.name', meta: { type: 'string' } }, // agent.name is already present in mockIndexPatternFields + { id: '_custom_field_0', name: '_custom_field_0', meta: { type: 'string' } }, + ] + : mockEsqlDatatable.columns + ) + ); + parseEsqlQueryMock.mockReturnValue({ isEsqlQueryAggregating: false }); }); it('should return loading true when esql fields still loading', () => { const { result } = renderHook( () => - useInvestigationFields({ + useAllEsqlRuleFields({ esqlQuery: mockEsqlQuery, indexPatternsFields: mockIndexPatternFields, }), @@ -68,71 +77,103 @@ describe('useInvestigationFields', () => { }); it('should return only index pattern fields when ES|QL query is empty', async () => { - const { result, waitForNextUpdate } = renderHook( + const { result } = renderHook( () => - useInvestigationFields({ + useAllEsqlRuleFields({ esqlQuery: '', indexPatternsFields: mockIndexPatternFields, }), { wrapper } ); - await waitForNextUpdate(); - - expect(result.current.investigationFields).toEqual(mockIndexPatternFields); + expect(result.current.fields).toEqual(mockIndexPatternFields); }); it('should return only index pattern fields when ES|QL query is undefined', async () => { const { result } = renderHook( () => - useInvestigationFields({ + useAllEsqlRuleFields({ esqlQuery: undefined, indexPatternsFields: mockIndexPatternFields, }), { wrapper } ); - expect(result.current.investigationFields).toEqual(mockIndexPatternFields); + expect(result.current.fields).toEqual(mockIndexPatternFields); }); it('should return index pattern fields concatenated with ES|QL fields when ES|QL query is non-aggregating', async () => { - computeIsESQLQueryAggregatingMock.mockReturnValue(false); + parseEsqlQueryMock.mockReturnValue({ isEsqlQueryAggregating: false }); - const { result } = renderHook( + const { result, waitFor } = renderHook( () => - useInvestigationFields({ + useAllEsqlRuleFields({ esqlQuery: mockEsqlQuery, indexPatternsFields: mockIndexPatternFields, }), { wrapper } ); - expect(result.current.investigationFields).toEqual([ - { - name: '_custom_field', - type: 'string', - }, - ...mockIndexPatternFields, - ]); + await waitFor(() => { + expect(result.current.fields).toEqual([ + { + name: '_custom_field', + type: 'string', + }, + ...mockIndexPatternFields, + ]); + }); }); it('should return only ES|QL fields when ES|QL query is aggregating', async () => { - computeIsESQLQueryAggregatingMock.mockReturnValue(true); + parseEsqlQueryMock.mockReturnValue({ isEsqlQueryAggregating: true }); - const { result } = renderHook( + const { result, waitFor } = renderHook( () => - useInvestigationFields({ + useAllEsqlRuleFields({ esqlQuery: mockEsqlQuery, indexPatternsFields: mockIndexPatternFields, }), { wrapper } ); + await waitFor(() => { + expect(result.current.fields).toEqual([ + { + name: '_custom_field', + type: 'string', + }, + ]); + }); + }); + + it('should deduplicate index pattern fields and ES|QL fields when fields have same name', async () => { + // getESQLQueryColumnsMock.mockClear(); + parseEsqlQueryMock.mockReturnValue({ isEsqlQueryAggregating: false }); + + const { result, waitFor } = renderHook( + () => + useAllEsqlRuleFields({ + esqlQuery: 'deduplicate_test', + indexPatternsFields: mockIndexPatternFields, + }), + { wrapper } + ); - expect(result.current.investigationFields).toEqual([ - { - name: '_custom_field', - type: 'string', - }, - ]); + await waitFor(() => { + expect(result.current.fields).toEqual([ + { + name: 'agent.name', + type: 'string', + }, + { + name: '_custom_field_0', + type: 'string', + }, + { + name: 'agent.type', + type: 'string', + }, + ]); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_all_esql_rule_fields.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_all_esql_rule_fields.ts new file mode 100644 index 0000000000000..a67b990c88b80 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_all_esql_rule_fields.ts @@ -0,0 +1,118 @@ +/* + * Copyright 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, useState } from 'react'; +import type { DatatableColumn } from '@kbn/expressions-plugin/public'; +import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import type { DataViewFieldBase } from '@kbn/es-query'; +import useDebounce from 'react-use/lib/useDebounce'; + +import { useQuery } from '@tanstack/react-query'; + +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { parseEsqlQuery } from '../../rule_creation/logic/esql_validator'; + +import { getEsqlQueryConfig } from '../../rule_creation/logic/get_esql_query_config'; + +const esqlToFields = ( + columns: { error: unknown } | DatatableColumn[] | undefined | null +): DataViewFieldBase[] => { + if (columns && 'error' in columns) { + return []; + } + + const fields = (columns ?? []).map(({ id, meta }) => { + return { + name: id, + type: meta.type, + }; + }); + + return fields; +}; + +type UseEsqlFields = (esqlQuery: string | undefined) => { + isLoading: boolean; + fields: DataViewFieldBase[]; +}; + +/** + * fetches ES|QL fields and convert them to DataViewBase fields + */ +export const useEsqlFields: UseEsqlFields = (esqlQuery) => { + const kibana = useKibana<{ data: DataPublicPluginStart }>(); + + const { data: dataService } = kibana.services; + + const queryConfig = getEsqlQueryConfig({ esqlQuery, search: dataService?.search?.search }); + const { data, isLoading } = useQuery(queryConfig); + + const fields = useMemo(() => { + return esqlToFields(data); + }, [data]); + + return { + fields, + isLoading, + }; +}; + +/** + * if ES|QL fields and index pattern fields have same name, duplicates will be removed and the rest of fields merged + * ES|QL fields are first in order, since these are the fields that returned in ES|QL response + * */ +const deduplicateAndMergeFields = ( + esqlFields: DataViewFieldBase[], + indexPatternsFields: DataViewFieldBase[] +) => { + const esqlFieldsSet = new Set(esqlFields.map((field) => field.name)); + return [...esqlFields, ...indexPatternsFields.filter((field) => !esqlFieldsSet.has(field.name))]; +}; + +type UseAllEsqlRuleFields = (params: { + esqlQuery: string | undefined; + indexPatternsFields: DataViewFieldBase[]; +}) => { + isLoading: boolean; + fields: DataViewFieldBase[]; +}; + +/** + * returns all fields available for ES|QL rule: + * - fields returned from ES|QL query for aggregating queries + * - fields returned from ES|QL query + index fields for non-aggregating queries + */ +export const useAllEsqlRuleFields: UseAllEsqlRuleFields = ({ esqlQuery, indexPatternsFields }) => { + const [debouncedEsqlQuery, setDebouncedEsqlQuery] = useState(undefined); + const { fields: esqlFields, isLoading } = useEsqlFields(debouncedEsqlQuery); + + const { isEsqlQueryAggregating } = useMemo( + () => parseEsqlQuery(debouncedEsqlQuery ?? ''), + [debouncedEsqlQuery] + ); + + useDebounce( + () => { + setDebouncedEsqlQuery(esqlQuery); + }, + 300, + [esqlQuery] + ); + + const fields = useMemo(() => { + if (!debouncedEsqlQuery) { + return indexPatternsFields; + } + return isEsqlQueryAggregating + ? esqlFields + : deduplicateAndMergeFields(esqlFields, indexPatternsFields); + }, [esqlFields, debouncedEsqlQuery, indexPatternsFields, isEsqlQueryAggregating]); + + return { + fields, + isLoading, + }; +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_esql_index.test.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_esql_index.test.ts index dc4394be257e5..2f5065eb113be 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_esql_index.test.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_esql_index.test.ts @@ -10,29 +10,28 @@ import { useEsqlIndex } from './use_esql_index'; const validEsqlQuery = 'from auditbeat* metadata _id, _index, _version'; describe('useEsqlIndex', () => { - it('should return empty array if isQueryReadEnabled is undefined', () => { - const { result } = renderHook(() => useEsqlIndex(validEsqlQuery, 'esql', undefined)); + it('should return parsed index array from a valid query', async () => { + const { result } = renderHook(() => useEsqlIndex(validEsqlQuery, 'esql')); - expect(result.current).toEqual([]); + expect(result.current).toEqual(['auditbeat*']); }); - it('should return empty array if isQueryReadEnabled is false', () => { - const { result } = renderHook(() => useEsqlIndex(validEsqlQuery, 'esql', false)); + it('should return empty array if rule type is not esql', async () => { + const { result } = renderHook(() => useEsqlIndex(validEsqlQuery, 'query')); expect(result.current).toEqual([]); }); - it('should return empty array if rule type is not esql', () => { - const { result } = renderHook(() => useEsqlIndex(validEsqlQuery, 'query', true)); - expect(result.current).toEqual([]); - }); - it('should return empty array if query is empty', () => { - const { result } = renderHook(() => useEsqlIndex('', 'esql', true)); + it('should return empty array if query is empty', async () => { + const { result } = renderHook(() => useEsqlIndex('', 'esql')); expect(result.current).toEqual([]); }); - it('should return parsed index array from a valid query', () => { - const { result } = renderHook(() => useEsqlIndex(validEsqlQuery, 'esql', true)); - expect(result.current).toEqual(['auditbeat*']); + it('should return empty array if invalid query is causing a TypeError in ES|QL parser', async () => { + const typeErrorCausingQuery = 'from auditbeat* []'; + + const { result } = renderHook(() => useEsqlIndex(typeErrorCausingQuery, 'esql')); + + expect(result.current).toEqual([]); }); }); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_esql_index.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_esql_index.ts index 508f2272b69ab..358c1fc70a945 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_esql_index.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_esql_index.ts @@ -4,7 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { useMemo } from 'react'; +import { useMemo, useState } from 'react'; +import useDebounce from 'react-use/lib/useDebounce'; import { getIndexPatternFromESQLQuery } from '@kbn/esql-utils'; import { getIndexListFromIndexString } from '@kbn/securitysolution-utils'; @@ -15,23 +16,39 @@ import { isEsqlRule } from '../../../../common/detection_engine/utils'; /** * parses ES|QL query and returns memoized array of indices - * @param query - ES|QL query to retrieve index from + * @param query - ES|QL query to retrieve indices from * @param ruleType - rule type value - * @param isQueryReadEnabled - if not enabled, return empty array. Useful if we know form or query is not valid and we don't want to retrieve index - * @returns + * @returns string[] - array of indices. Array is empty if query is invalid or ruleType is not 'esql'. */ -export const useEsqlIndex = ( - query: Query['query'], - ruleType: Type, - isQueryReadEnabled: boolean | undefined -) => { +export const useEsqlIndex = (query: Query['query'], ruleType: Type): string[] => { + const [debouncedQuery, setDebouncedQuery] = useState(query); + + useDebounce( + () => { + /* + Triggerring the ES|QL parser a few moments after the user has finished typing + to avoid unnecessary calls to the parser. + */ + setDebouncedQuery(query); + }, + 300, + [query] + ); + const indexString = useMemo(() => { - if (!isQueryReadEnabled) { + const esqlQuery = + typeof debouncedQuery === 'string' && isEsqlRule(ruleType) ? debouncedQuery : undefined; + + try { + return getIndexPatternFromESQLQuery(esqlQuery); + } catch (error) { + /* + Some invalid queries cause ES|QL parser to throw a TypeError. + Treating such cases as if parser returned an empty string. + */ return ''; } - const esqlQuery = typeof query === 'string' && isEsqlRule(ruleType) ? query : undefined; - return getIndexPatternFromESQLQuery(esqlQuery); - }, [query, isQueryReadEnabled, ruleType]); + }, [debouncedQuery, ruleType]); const index = useMemo(() => getIndexListFromIndexString(indexString), [indexString]); return index; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_investigation_fields.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_investigation_fields.ts deleted file mode 100644 index 1627bddaa82be..0000000000000 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_investigation_fields.ts +++ /dev/null @@ -1,92 +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 type { DatatableColumn } from '@kbn/expressions-plugin/public'; -import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; -import type { DataViewFieldBase } from '@kbn/es-query'; -import { computeIsESQLQueryAggregating } from '@kbn/securitysolution-utils'; - -import { useQuery } from '@tanstack/react-query'; - -import { useKibana } from '@kbn/kibana-react-plugin/public'; - -import { getEsqlQueryConfig } from '../../rule_creation/logic/get_esql_query_config'; - -const esqlToFields = ( - columns: { error: unknown } | DatatableColumn[] | undefined | null -): DataViewFieldBase[] => { - if (columns && 'error' in columns) { - return []; - } - - const fields = (columns ?? []).map(({ id, meta }) => { - return { - name: id, - type: meta.type, - }; - }); - - return fields; -}; - -type UseEsqlFields = (esqlQuery: string | undefined) => { - isLoading: boolean; - fields: DataViewFieldBase[]; -}; - -/** - * fetches ES|QL fields and convert them to DataViewBase fields - */ -const useEsqlFields: UseEsqlFields = (esqlQuery) => { - const kibana = useKibana<{ data: DataPublicPluginStart }>(); - - const { data: dataService } = kibana.services; - - const queryConfig = getEsqlQueryConfig({ esqlQuery, search: dataService?.search?.search }); - const { data, isLoading } = useQuery(queryConfig); - - const fields = useMemo(() => { - return esqlToFields(data); - }, [data]); - - return { - fields, - isLoading, - }; -}; - -type UseInvestigationFields = (params: { - esqlQuery: string | undefined; - indexPatternsFields: DataViewFieldBase[]; -}) => { - isLoading: boolean; - investigationFields: DataViewFieldBase[]; -}; - -export const useInvestigationFields: UseInvestigationFields = ({ - esqlQuery, - indexPatternsFields, -}) => { - const { fields: esqlFields, isLoading } = useEsqlFields(esqlQuery); - - const investigationFields = useMemo(() => { - if (!esqlQuery) { - return indexPatternsFields; - } - - // alerts generated from non-aggregating queries are enriched with source document - // so, index patterns fields should be included in the list of investigation fields - const isEsqlQueryAggregating = computeIsESQLQueryAggregating(esqlQuery); - - return isEsqlQueryAggregating ? esqlFields : [...esqlFields, ...indexPatternsFields]; - }, [esqlFields, esqlQuery, indexPatternsFields]); - - return { - investigationFields, - isLoading, - }; -}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.test.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.test.ts index 20a432cdc1420..b61cdbc386ee1 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.test.ts @@ -140,6 +140,7 @@ describe('helpers', () => { version: '^1.2.3', }, ], + required_fields: [{ name: 'host.name', type: 'keyword' }], }; expect(result).toEqual(expected); @@ -178,6 +179,20 @@ describe('helpers', () => { }); }); + test('filters out empty required fields', () => { + const result = formatDefineStepData({ + ...mockData, + requiredFields: [ + { name: 'host.name', type: 'keyword' }, + { name: '', type: '' }, + ], + }); + + expect(result).toMatchObject({ + required_fields: [{ name: 'host.name', type: 'keyword' }], + }); + }); + describe('saved_query and query rule types', () => { test('returns query rule if savedId provided but shouldLoadQueryDynamically != true', () => { const mockStepData: DefineStepRule = { @@ -567,6 +582,7 @@ describe('helpers', () => { version: '^1.2.3', }, ], + required_fields: [{ name: 'host.name', type: 'keyword' }], }; expect(result).toEqual(expected); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts index 3054a894d0df1..f281b3b6b4a2b 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts @@ -48,6 +48,7 @@ import { import type { RuleCreateProps, AlertSuppression, + RequiredFieldInput, } from '../../../../../common/api/detection_engine/model/rule_schema'; import { stepActionsDefaultValue } from '../../../rule_creation/components/step_rule_actions'; import { DEFAULT_SUPPRESSION_MISSING_FIELDS_STRATEGY } from '../../../../../common/detection_engine/constants'; @@ -395,6 +396,12 @@ export const getStepDataDataSource = ( return copiedStepData; }; +/** + * Strips away form rows that were not filled out by the user + */ +const removeEmptyRequiredFields = (requiredFields: RequiredFieldInput[]): RequiredFieldInput[] => + requiredFields.filter((field) => field.name !== '' && field.type !== ''); + export const formatDefineStepData = (defineStepData: DefineStepRule): DefineStepRuleJson => { const stepData = getStepDataDataSource(defineStepData); @@ -426,6 +433,8 @@ export const formatDefineStepData = (defineStepData: DefineStepRule): DefineStep } : {}; + const requiredFields = removeEmptyRequiredFields(defineStepData.requiredFields ?? []); + const typeFields = isMlFields(ruleFields) ? { anomaly_threshold: ruleFields.anomalyThreshold, @@ -438,6 +447,7 @@ export const formatDefineStepData = (defineStepData: DefineStepRule): DefineStep language: ruleFields.queryBar?.query?.language, query: ruleFields.queryBar?.query?.query as string, saved_id: ruleFields.queryBar?.saved_id ?? undefined, + required_fields: requiredFields, ...(ruleType === 'threshold' && { threshold: { field: ruleFields.threshold?.field ?? [], @@ -465,6 +475,7 @@ export const formatDefineStepData = (defineStepData: DefineStepRule): DefineStep language: ruleFields.queryBar?.query?.language, query: ruleFields.queryBar?.query?.query as string, saved_id: ruleFields.queryBar?.saved_id ?? undefined, + required_fields: requiredFields, threat_index: ruleFields.threatIndex, threat_query: ruleFields.threatQueryBar?.query?.query as string, threat_filters: ruleFields.threatQueryBar?.filters, @@ -479,6 +490,7 @@ export const formatDefineStepData = (defineStepData: DefineStepRule): DefineStep language: ruleFields.queryBar?.query?.language, query: ruleFields.queryBar?.query?.query as string, saved_id: ruleFields.queryBar?.saved_id ?? undefined, + required_fields: requiredFields, timestamp_field: ruleFields.eqlOptions?.timestampField, event_category_override: ruleFields.eqlOptions?.eventCategoryField, tiebreaker_field: ruleFields.eqlOptions?.tiebreakerField, @@ -490,6 +502,7 @@ export const formatDefineStepData = (defineStepData: DefineStepRule): DefineStep filters: ruleFields.queryBar?.filters, language: ruleFields.queryBar?.query?.language, query: ruleFields.queryBar?.query?.query as string, + required_fields: requiredFields, new_terms_fields: ruleFields.newTermsFields, history_window_start: `now-${ruleFields.historyWindowSize}`, ...alertSuppressionFields, @@ -498,6 +511,8 @@ export const formatDefineStepData = (defineStepData: DefineStepRule): DefineStep ? { language: ruleFields.queryBar?.query?.language, query: ruleFields.queryBar?.query?.query as string, + required_fields: requiredFields, + ...alertSuppressionFields, } : { ...alertSuppressionFields, @@ -506,6 +521,7 @@ export const formatDefineStepData = (defineStepData: DefineStepRule): DefineStep language: ruleFields.queryBar?.query?.language, query: ruleFields.queryBar?.query?.query as string, saved_id: undefined, + required_fields: requiredFields, type: 'query' as const, // rule only be updated as saved_query type if it has saved_id and shouldLoadQueryDynamically checkbox checked ...(['query', 'saved_query'].includes(ruleType) && diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/index.tsx index d57198522b388..806ea9f336bd5 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/index.tsx @@ -210,11 +210,9 @@ const CreateRulePageComponent: React.FC = () => { const [isThreatQueryBarValid, setIsThreatQueryBarValid] = useState(false); const esqlQueryForAboutStep = useEsqlQueryForAboutStep({ defineStepData, activeStep }); - const esqlIndex = useEsqlIndex( - defineStepData.queryBar.query.query, - ruleType, - defineStepForm.isValid - ); + + const esqlIndex = useEsqlIndex(defineStepData.queryBar.query.query, ruleType); + const memoizedIndex = useMemo( () => (isEsqlRuleValue ? esqlIndex : defineStepData.index), [defineStepData.index, esqlIndex, isEsqlRuleValue] diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_editing/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_editing/index.tsx index 768b5a9903691..47b67c8ed720a 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_editing/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_editing/index.tsx @@ -150,13 +150,9 @@ const EditRulePageComponent: FC<{ rule: RuleResponse }> = ({ rule }) => { }); const esqlQueryForAboutStep = useEsqlQueryForAboutStep({ defineStepData, activeStep }); - const esqlIndex = useEsqlIndex( - defineStepData.queryBar.query.query, - defineStepData.ruleType, - // allow to compute index from query only when query is valid or user switched to another tab - // to prevent multiple data view initiations with partly typed index names - defineStepForm.isValid || activeStep !== RuleStep.defineRule - ); + + const esqlIndex = useEsqlIndex(defineStepData.queryBar.query.query, defineStepData.ruleType); + const memoizedIndex = useMemo( () => (isEsqlRule(defineStepData.ruleType) ? esqlIndex : defineStepData.index), [defineStepData.index, esqlIndex, defineStepData.ruleType] diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/required_field_icon.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/required_field_icon.tsx new file mode 100644 index 0000000000000..0001bae25dd89 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/required_field_icon.tsx @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { ES_FIELD_TYPES } from '@kbn/field-types'; +import { FieldIcon } from '@kbn/field-utils'; +import type { FieldIconProps } from '@kbn/field-utils'; + +function mapEsTypesToIconProps(type: string) { + switch (type) { + case ES_FIELD_TYPES._ID: + case ES_FIELD_TYPES._INDEX: + /* In Discover "_id" and "_index" have the "keyword" icon. Doing same here for consistency */ + return { type: 'keyword' }; + case ES_FIELD_TYPES.OBJECT: + return { type, iconType: 'tokenObject' }; + case ES_FIELD_TYPES.DATE_NANOS: + return { type: 'date' }; + case ES_FIELD_TYPES.FLOAT: + case ES_FIELD_TYPES.HALF_FLOAT: + case ES_FIELD_TYPES.SCALED_FLOAT: + case ES_FIELD_TYPES.DOUBLE: + case ES_FIELD_TYPES.INTEGER: + case ES_FIELD_TYPES.LONG: + case ES_FIELD_TYPES.SHORT: + case ES_FIELD_TYPES.UNSIGNED_LONG: + case ES_FIELD_TYPES.AGGREGATE_METRIC_DOUBLE: + case ES_FIELD_TYPES.FLOAT_RANGE: + case ES_FIELD_TYPES.DOUBLE_RANGE: + case ES_FIELD_TYPES.INTEGER_RANGE: + case ES_FIELD_TYPES.LONG_RANGE: + case ES_FIELD_TYPES.BYTE: + case ES_FIELD_TYPES.TOKEN_COUNT: + return { type: 'number' }; + default: + return { type }; + } +} + +interface RequiredFieldIconProps extends FieldIconProps { + type: string; + label?: string; + 'data-test-subj': string; +} + +/** + * `FieldIcon` component with addtional icons for types that are not handled by the `FieldIcon` component. + */ +export function RequiredFieldIcon({ + type, + label = type, + 'data-test-subj': dataTestSubj, + ...props +}: RequiredFieldIconProps) { + return ( + + ); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_definition_section.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_definition_section.tsx index 912af1b0c4589..35f429f776fab 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_definition_section.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_definition_section.tsx @@ -24,8 +24,6 @@ import type { Filter } from '@kbn/es-query'; import type { SavedQuery } from '@kbn/data-plugin/public'; import { mapAndFlattenFilters } from '@kbn/data-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/public'; -import { FieldIcon } from '@kbn/react-field'; -import { castEsToKbnFieldTypeName } from '@kbn/field-types'; import { FilterItems } from '@kbn/unified-search-plugin/public'; import type { AlertSuppressionMissingFieldsStrategy, @@ -53,6 +51,7 @@ import { BadgeList } from './badge_list'; import { DEFAULT_DESCRIPTION_LIST_COLUMN_WIDTHS } from './constants'; import * as i18n from './translations'; import { useAlertSuppression } from '../../logic/use_alert_suppression'; +import { RequiredFieldIcon } from './required_field_icon'; import { filtersStyles, queryStyles, @@ -254,17 +253,14 @@ interface RequiredFieldsProps { const RequiredFields = ({ requiredFields }: RequiredFieldsProps) => { const styles = useRequiredFieldsStyles(); + return ( {requiredFields.map((rF, index) => ( - + { expect(result.current.isSuppressionEnabled).toBe(false); }); + + it('should return isSuppressionEnabled false if ES|QL Feature Flag is disabled', () => { + const { result } = renderHook(() => useAlertSuppression('esql')); + + expect(result.current.isSuppressionEnabled).toBe(false); + }); + + it('should return isSuppressionEnabled true if ES|QL Feature Flag is enabled', () => { + jest + .spyOn(useIsExperimentalFeatureEnabledMock, 'useIsExperimentalFeatureEnabled') + .mockImplementation((flag) => flag === 'alertSuppressionForEsqlRuleEnabled'); + const { result } = renderHook(() => useAlertSuppression('esql')); + + expect(result.current.isSuppressionEnabled).toBe(true); + }); }); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/use_alert_suppression.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/use_alert_suppression.tsx index 6e1b2a4d6163f..1c9f139633c8c 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/use_alert_suppression.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/use_alert_suppression.tsx @@ -7,19 +7,28 @@ import { useCallback } from 'react'; import type { Type } from '@kbn/securitysolution-io-ts-alerting-types'; import { isSuppressibleAlertRule } from '../../../../common/detection_engine/utils'; +import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features'; export interface UseAlertSuppressionReturn { isSuppressionEnabled: boolean; } export const useAlertSuppression = (ruleType: Type | undefined): UseAlertSuppressionReturn => { + const isAlertSuppressionForEsqlRuleEnabled = useIsExperimentalFeatureEnabled( + 'alertSuppressionForEsqlRuleEnabled' + ); + const isSuppressionEnabledForRuleType = useCallback(() => { if (!ruleType) { return false; } + // Remove this condition when the Feature Flag for enabling Suppression in the New terms rule is removed. + if (ruleType === 'esql') { + return isSuppressibleAlertRule(ruleType) && isAlertSuppressionForEsqlRuleEnabled; + } return isSuppressibleAlertRule(ruleType); - }, [ruleType]); + }, [ruleType, isAlertSuppressionForEsqlRuleEnabled]); return { isSuppressionEnabled: isSuppressionEnabledForRuleType(), diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts index 7552ac9b711f3..0de6e5d1e0844 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts @@ -216,7 +216,7 @@ export const mockDefineStepRule = (): DefineStepRule => ({ dataViewId: undefined, queryBar: mockQueryBar, threatQueryBar: mockQueryBar, - requiredFields: [], + requiredFields: [{ name: 'host.name', type: 'keyword' }], relatedIntegrations: [ { package: 'aws', diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table_toolbar.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table_toolbar.tsx index fb2c17ddb2fa1..e3618a1383598 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table_toolbar.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table_toolbar.tsx @@ -78,7 +78,7 @@ export const RulesTableToolbar = React.memo(() => { ); // Assistant integration for using selected rules as prompt context - const { hasAssistantPrivilege } = useAssistantAvailability(); + const { hasAssistantPrivilege, isAssistantEnabled } = useAssistantAvailability(); const { state: { rules, selectedRuleIds }, } = useRulesTableContext(); @@ -105,6 +105,7 @@ export const RulesTableToolbar = React.memo(() => { getPromptContext={getPromptContext} suggestedUserPrompt={i18nAssistant.EXPLAIN_THEN_SUMMARIZE_RULE_DETAILS} tooltip={i18nAssistant.RULE_MANAGEMENT_CONTEXT_TOOLTIP} + isAssistantEnabled={isAssistantEnabled} /> )} diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx index 3a8153d9fa2b3..7a3ed25b8084e 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx @@ -42,7 +42,7 @@ import { ALERT_NEW_TERMS, ALERT_RULE_INDICES, } from '../../../../common/field_maps/field_names'; -import { isEqlRule } from '../../../../common/detection_engine/utils'; +import { isEqlRule, isEsqlRule } from '../../../../common/detection_engine/utils'; import type { TimelineResult } from '../../../../common/api/timeline'; import { TimelineId } from '../../../../common/types/timeline'; import { TimelineStatus, TimelineType } from '../../../../common/api/timeline'; @@ -279,6 +279,11 @@ export const isEqlAlert = (ecsData: Ecs): boolean => { return isEqlRule(ruleType) || (Array.isArray(ruleType) && isEqlRule(ruleType[0])); }; +export const isEsqlAlert = (ecsData: Ecs): boolean => { + const ruleType = getField(ecsData, ALERT_RULE_TYPE); + return isEsqlRule(ruleType) || (Array.isArray(ruleType) && isEsqlRule(ruleType[0])); +}; + export const isNewTermsAlert = (ecsData: Ecs): boolean => { const ruleType = getField(ecsData, ALERT_RULE_TYPE); return ( @@ -1026,8 +1031,8 @@ export const sendAlertToTimelineAction = async ({ }, getExceptionFilter ); - // The Query field should remain unpopulated with the suppressed EQL alert. - } else if (isSuppressedAlert(ecsData) && !isEqlAlert(ecsData)) { + // The Query field should remain unpopulated with the suppressed EQL/ES|QL alert. + } else if (isSuppressedAlert(ecsData) && !isEqlAlert(ecsData) && !isEsqlAlert(ecsData)) { return createSuppressedTimeline( ecsData, createTimeline, @@ -1097,8 +1102,8 @@ export const sendAlertToTimelineAction = async ({ return createThresholdTimeline(ecsData, createTimeline, noteContent, {}, getExceptionFilter); } else if (isNewTermsAlert(ecsData)) { return createNewTermsTimeline(ecsData, createTimeline, noteContent, {}, getExceptionFilter); - // The Query field should remain unpopulated with the suppressed EQL alert. - } else if (isSuppressedAlert(ecsData) && !isEqlAlert(ecsData)) { + // The Query field should remain unpopulated with the suppressed EQL/ES|QL alert. + } else if (isSuppressedAlert(ecsData) && !isEqlAlert(ecsData) && !isEsqlAlert(ecsData)) { return createSuppressedTimeline(ecsData, createTimeline, noteContent, {}, getExceptionFilter); } else { let { dataProviders, filters } = buildTimelineDataProviderOrFilter( diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_execution_status/rule_status_failed_callout.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_execution_status/rule_status_failed_callout.tsx index ba7f57aba5648..170cdaf265d4a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_execution_status/rule_status_failed_callout.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_execution_status/rule_status_failed_callout.tsx @@ -33,7 +33,7 @@ const RuleStatusFailedCallOutComponent: React.FC = message, status, }) => { - const { hasAssistantPrivilege } = useAssistantAvailability(); + const { hasAssistantPrivilege, isAssistantEnabled } = useAssistantAvailability(); const { shouldBeDisplayed, color, title } = getPropsByStatus(status); const getPromptContext = useCallback( async () => @@ -84,6 +84,7 @@ const RuleStatusFailedCallOutComponent: React.FC = getPromptContext={getPromptContext} suggestedUserPrompt={i18n.ASK_ASSISTANT_USER_PROMPT} tooltip={i18n.ASK_ASSISTANT_TOOLTIP} + isAssistantEnabled={isAssistantEnabled} > {i18n.ASK_ASSISTANT_ERROR_BUTTON} diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts index 4757c9f29dfdc..1bf915e1a122f 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts @@ -26,7 +26,6 @@ import type { FieldValueThreshold } from '../../../../detection_engine/rule_crea import type { BuildingBlockType, RelatedIntegrationArray, - RequiredFieldArray, RuleAuthorArray, RuleLicense, RuleNameOverride, @@ -38,6 +37,7 @@ import type { AlertSuppression, ThresholdAlertSuppression, RelatedIntegration, + RequiredFieldInput, } from '../../../../../common/api/detection_engine/model/rule_schema'; import type { SortOrder } from '../../../../../common/api/detection_engine'; import type { EqlOptionsSelected } from '../../../../../common/search_strategy'; @@ -147,7 +147,7 @@ export interface DefineStepRule { dataViewId?: string; dataViewTitle?: string; relatedIntegrations?: RelatedIntegrationArray; - requiredFields: RequiredFieldArray; + requiredFields?: RequiredFieldInput[]; ruleType: Type; timeline: FieldValueTimeline; threshold: FieldValueThreshold; @@ -226,6 +226,7 @@ export interface DefineStepRuleJson { tiebreaker_field?: string; alert_suppression?: AlertSuppression | ThresholdAlertSuppression; related_integrations?: RelatedIntegration[]; + required_fields?: RequiredFieldInput[]; } export interface AboutStepRuleJson { diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/index.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/index.tsx index 71ab20090ff01..edf7bce0a9938 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/index.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/index.tsx @@ -160,7 +160,7 @@ const EntityAnalyticsRiskScoresComponent = ({ riskEntity }: { riskEntity: RiskSc const refreshPage = useRefetchQueries(); - const privileges = useMissingRiskEnginePrivileges(); + const privileges = useMissingRiskEnginePrivileges(['read']); if (!isAuthorized) { return null; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/hooks/use_missing_risk_engine_privileges.ts b/x-pack/plugins/security_solution/public/entity_analytics/hooks/use_missing_risk_engine_privileges.ts index d51c2b2dfb49d..9fa4c8d4b3881 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/hooks/use_missing_risk_engine_privileges.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/hooks/use_missing_risk_engine_privileges.ts @@ -6,9 +6,13 @@ */ import { useMemo } from 'react'; +import type { NonEmptyArray } from 'fp-ts/lib/NonEmptyArray'; import { useRiskEnginePrivileges } from '../api/hooks/use_risk_engine_privileges'; import { getMissingRiskEnginePrivileges } from '../../../common/entity_analytics/risk_engine'; -import type { MissingPrivileges } from '../../../common/entity_analytics/risk_engine'; +import type { + MissingPrivileges, + RiskEngineIndexPrivilege, +} from '../../../common/entity_analytics/risk_engine'; export type RiskEngineMissingPrivilegesResponse = | { isLoading: true } | { isLoading: false; hasAllRequiredPrivileges: true } @@ -18,7 +22,9 @@ export type RiskEngineMissingPrivilegesResponse = hasAllRequiredPrivileges: false; }; -export const useMissingRiskEnginePrivileges = (): RiskEngineMissingPrivilegesResponse => { +export const useMissingRiskEnginePrivileges = ( + required: NonEmptyArray = ['read', 'write'] +): RiskEngineMissingPrivilegesResponse => { const { data: privilegesResponse, isLoading } = useRiskEnginePrivileges(); return useMemo(() => { @@ -36,9 +42,19 @@ export const useMissingRiskEnginePrivileges = (): RiskEngineMissingPrivilegesRes } const { indexPrivileges, clusterPrivileges } = getMissingRiskEnginePrivileges( - privilegesResponse.privileges + privilegesResponse.privileges, + required ); + // privilegesResponse.has_all_required` is slightly misleading, it checks if it has *all* default required privileges. + // Here we check if there are no missing privileges of the provided set of required privileges + if (indexPrivileges.every(([_, missingPrivileges]) => missingPrivileges.length === 0)) { + return { + isLoading: false, + hasAllRequiredPrivileges: true, + }; + } + return { isLoading: false, hasAllRequiredPrivileges: false, @@ -47,5 +63,5 @@ export const useMissingRiskEnginePrivileges = (): RiskEngineMissingPrivilegesRes clusterPrivileges, }, }; - }, [isLoading, privilegesResponse]); + }, [isLoading, privilegesResponse, required]); }; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/hooks/use_assistant.ts b/x-pack/plugins/security_solution/public/flyout/document_details/right/hooks/use_assistant.ts index 185591ff43a2d..2196674964cfe 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/hooks/use_assistant.ts +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/hooks/use_assistant.ts @@ -56,7 +56,7 @@ export const useAssistant = ({ dataFormattedForFieldBrowser, isAlert, }: UseAssistantParams): UseAssistantResult => { - const { hasAssistantPrivilege } = useAssistantAvailability(); + const { hasAssistantPrivilege, isAssistantEnabled } = useAssistantAvailability(); const useAssistantHook = hasAssistantPrivilege ? useAssistantOverlay : useAssistantNoop; const getPromptContext = useCallback( async () => getRawData(dataFormattedForFieldBrowser ?? []), @@ -73,7 +73,8 @@ export const useAssistant = ({ isAlert ? PROMPT_CONTEXTS[PROMPT_CONTEXT_ALERT_CATEGORY].suggestedUserPrompt : PROMPT_CONTEXTS[PROMPT_CONTEXT_EVENT_CATEGORY].suggestedUserPrompt, - isAlert ? ALERT_SUMMARY_VIEW_CONTEXT_TOOLTIP : EVENT_SUMMARY_VIEW_CONTEXT_TOOLTIP + isAlert ? ALERT_SUMMARY_VIEW_CONTEXT_TOOLTIP : EVENT_SUMMARY_VIEW_CONTEXT_TOOLTIP, + isAssistantEnabled ); return { diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifact_tabs_in_policy_details.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifact_tabs_in_policy_details.cy.ts index dedbbf607b3f0..4aa8085fbf571 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifact_tabs_in_policy_details.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifact_tabs_in_policy_details.cy.ts @@ -83,7 +83,9 @@ describe( }); for (const testData of getArtifactsListTestsData()) { - describe(`${testData.title} tab`, () => { + // FLAKY: https://github.com/elastic/kibana/issues/183670 + // FLAKY: https://github.com/elastic/kibana/issues/183671 + describe.skip(`${testData.title} tab`, () => { beforeEach(() => { login(); removeExceptionsList(testData.createRequestBody.list_id); diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifacts_mocked_data.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifacts_mocked_data.cy.ts index 9227eb80153fb..22a796767a530 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifacts_mocked_data.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifacts_mocked_data.cy.ts @@ -50,7 +50,10 @@ describe('Artifacts pages', { tags: ['@ess', '@serverless', '@skipInServerlessMK }); for (const testData of getArtifactsListTestsData()) { - describe(`When on the ${testData.title} entries list`, () => { + // FLAKY: https://github.com/elastic/kibana/issues/183718 + // FLAKY: https://github.com/elastic/kibana/issues/183719 + // FLAKY: https://github.com/elastic/kibana/issues/183720 + describe.skip(`When on the ${testData.title} entries list`, () => { it(`no access - should show no privileges callout`, () => { loginWithoutAccess(`/app/security/administration/${testData.urlPath}`); cy.getByTestSubj('noPrivilegesPage').should('exist'); diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/policy/policy_details.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/policy/policy_details.cy.ts index 0bc6e907fe827..c587c9b9cf8e2 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/policy/policy_details.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/policy/policy_details.cy.ts @@ -38,6 +38,17 @@ describe( }, }, () => { + const getAutomaticUpdatesToggle = () => cy.getByTestSubj('protection-updates-manifest-switch'); + const clickAutomaticUpdatesToggle = () => getAutomaticUpdatesToggle().click(); + + const getProtectionUpdatesSaveButton = () => cy.getByTestSubj('protectionUpdatesSaveButton'); + const clickProtectionUpdatesSaveButton = () => getProtectionUpdatesSaveButton().click(); + + const [expectSavedButtonToBeDisabled, expectSavedButtonToBeEnabled] = [ + () => getProtectionUpdatesSaveButton().should('be.disabled'), + () => getProtectionUpdatesSaveButton().should('be.enabled'), + ]; + describe('Protection updates', () => { const loadProtectionUpdatesUrl = (policyId: string) => loadPage(`/app/security/administration/policy/${policyId}/protectionUpdates`); @@ -71,11 +82,10 @@ describe( loadProtectionUpdatesUrl(policy.id); cy.getByTestSubj('protection-updates-warning-callout'); cy.getByTestSubj('protection-updates-automatic-updates-enabled'); - cy.getByTestSubj('protection-updates-manifest-switch'); + getAutomaticUpdatesToggle(); cy.getByTestSubj('protection-updates-manifest-name-title'); - cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled'); - - cy.getByTestSubj('protection-updates-manifest-switch').click(); + expectSavedButtonToBeDisabled(); + clickAutomaticUpdatesToggle(); cy.getByTestSubj('protection-updates-manifest-name-deployed-version-title'); cy.getByTestSubj('protection-updates-deployed-version').contains('latest'); @@ -85,12 +95,12 @@ describe( }); cy.getByTestSubj('protection-updates-manifest-name-note-title'); cy.getByTestSubj('protection-updates-manifest-note'); - cy.getByTestSubj('protectionUpdatesSaveButton').should('be.enabled'); + expectSavedButtonToBeEnabled(); }); it('should display warning modal when user has unsaved changes', () => { loadProtectionUpdatesUrl(policy.id); - cy.getByTestSubj('protection-updates-manifest-switch').click(); + clickAutomaticUpdatesToggle(); cy.getByTestSubj('policySettingsTab').click(); cy.getByTestSubj('policyDetailsUnsavedChangesModal').within(() => { cy.getByTestSubj('confirmModalCancelButton').click(); @@ -104,17 +114,25 @@ describe( }); it('should successfully update the manifest version to custom date', () => { + const todayMinusTwoDays = moment.utc().subtract(2, 'days'); + loadProtectionUpdatesUrl(policy.id); - cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled'); - cy.getByTestSubj('protection-updates-manifest-switch').click(); + expectSavedButtonToBeDisabled(); + clickAutomaticUpdatesToggle(); cy.getByTestSubj('protection-updates-manifest-note').type(testNote); + cy.getByTestSubj('protection-updates-version-to-deploy-picker').find('input').clear(); + + cy.getByTestSubj('protection-updates-version-to-deploy-picker') + .find('input') + .type(todayMinusTwoDays.format('MMMM DD, YYYY')); + cy.intercept('PUT', `/api/fleet/package_policies/${policy.id}`).as('policy'); cy.intercept('POST', `/api/endpoint/protection_updates_note/${policy.id}`).as('note'); - cy.getByTestSubj('protectionUpdatesSaveButton').click(); + clickProtectionUpdatesSaveButton(); cy.wait('@policy').then(({ request, response }) => { expect(request.body.inputs[0].config.policy.value.global_manifest_version).to.equal( - defaultDate.format('YYYY-MM-DD') + todayMinusTwoDays.format('YYYY-MM-DD') ); expect(response?.statusCode).to.equal(200); }); @@ -125,9 +143,23 @@ describe( }); cy.getByTestSubj('protectionUpdatesSuccessfulMessage'); - cy.getByTestSubj('protection-updates-deployed-version').contains(formattedDefaultDate); + cy.getByTestSubj('protection-updates-deployed-version').contains( + todayMinusTwoDays.format('MMMM DD, YYYY') + ); + cy.getByTestSubj('protection-updates-manifest-note').contains(testNote); + expectSavedButtonToBeDisabled(); + + // Reload page, make sure the changes are persisted + loadProtectionUpdatesUrl(policy.id); + // Date shouldn't match today, so the button should be enabled + expectSavedButtonToBeEnabled(); + cy.getByTestSubj('protection-updates-deployed-version').contains( + todayMinusTwoDays.format('MMMM DD, YYYY') + ); cy.getByTestSubj('protection-updates-manifest-note').contains(testNote); - cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled'); + + clickProtectionUpdatesSaveButton(); + expectSavedButtonToBeDisabled(); }); }); @@ -155,14 +187,23 @@ describe( } }); - it('should update manifest version to latest when enabling automatic updates', () => { + it('should NOT display warning modal when user enters the page with previously selected custom date', () => { loadProtectionUpdatesUrl(policy.id); - cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled'); + // The button is enabled because today is not the same as the date in the policy + expectSavedButtonToBeEnabled(); + + cy.getByTestSubj('policySettingsTab').click(); + cy.getByTestSubj('policyDetailsUnsavedChangesModal').should('not.exist'); + cy.url().should('include', 'settings'); + }); - cy.getByTestSubj('protection-updates-manifest-switch').click(); + it('should update manifest version to latest when enabling automatic updates', () => { + loadProtectionUpdatesUrl(policy.id); + expectSavedButtonToBeEnabled(); + clickAutomaticUpdatesToggle(); cy.intercept('PUT', `/api/fleet/package_policies/${policy.id}`).as('policy_latest'); - cy.getByTestSubj('protectionUpdatesSaveButton').click(); + clickProtectionUpdatesSaveButton(); cy.wait('@policy_latest').then(({ request, response }) => { expect(request.body.inputs[0].config.policy.value.global_manifest_version).to.equal( @@ -172,7 +213,7 @@ describe( }); cy.getByTestSubj('protectionUpdatesSuccessfulMessage'); cy.getByTestSubj('protection-updates-automatic-updates-enabled'); - cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled'); + expectSavedButtonToBeDisabled(); }); }); @@ -203,7 +244,7 @@ describe( it('should update note on save', () => { loadProtectionUpdatesUrl(policy.id); - cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled'); + expectSavedButtonToBeEnabled(); cy.getByTestSubj('protection-updates-manifest-note').contains(testNote); cy.getByTestSubj('protection-updates-manifest-note').clear(); @@ -212,14 +253,14 @@ describe( cy.intercept('POST', `/api/endpoint/protection_updates_note/${policy.id}`).as( 'note_updated' ); - cy.getByTestSubj('protectionUpdatesSaveButton').click(); + clickProtectionUpdatesSaveButton(); cy.wait('@note_updated').then(({ request, response }) => { expect(request.body.note).to.equal(updatedTestNote); expect(response?.statusCode).to.equal(200); }); cy.getByTestSubj('protectionUpdatesSuccessfulMessage'); cy.getByTestSubj('protection-updates-manifest-note').contains(updatedTestNote); - cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled'); + expectSavedButtonToBeDisabled(); loadProtectionUpdatesUrl(policy.id); cy.getByTestSubj('protection-updates-manifest-note').contains(updatedTestNote); @@ -256,7 +297,7 @@ describe( it('should render the protection updates tab content', () => { loadProtectionUpdatesUrl(policy.id); - cy.getByTestSubj('protection-updates-manifest-switch').should('not.exist'); + getAutomaticUpdatesToggle().should('not.exist'); cy.getByTestSubj('protection-updates-state-view-mode'); cy.getByTestSubj('protection-updates-manifest-name-title'); @@ -271,7 +312,7 @@ describe( cy.getByTestSubj('protection-updates-manifest-name-note-title'); cy.getByTestSubj('protection-updates-manifest-note').should('not.exist'); cy.getByTestSubj('protection-updates-manifest-note-view-mode').contains(testNote); - cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled'); + expectSavedButtonToBeDisabled(); }); }); @@ -303,7 +344,7 @@ describe( it('should render the protection updates tab content', () => { loadProtectionUpdatesUrl(policy.id); - cy.getByTestSubj('protection-updates-manifest-switch').should('not.exist'); + getAutomaticUpdatesToggle().should('not.exist'); cy.getByTestSubj('protection-updates-state-view-mode'); cy.getByTestSubj('protection-updates-manifest-name-title'); @@ -318,7 +359,7 @@ describe( cy.getByTestSubj('protection-updates-manifest-name-note-title').should('not.exist'); cy.getByTestSubj('protection-updates-manifest-note').should('not.exist'); cy.getByTestSubj('protection-updates-manifest-note-view-mode').should('not.exist'); - cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled'); + expectSavedButtonToBeDisabled(); }); }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_settings_form/components/cards/malware_protections_card.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_settings_form/components/cards/malware_protections_card.tsx index 65be904ec649b..f1eda1ac0915e 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_settings_form/components/cards/malware_protections_card.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_settings_form/components/cards/malware_protections_card.tsx @@ -180,7 +180,7 @@ export const MalwareProtectionsCard = React.memo( mode={mode} data-test-subj={getTestId('onWriteScan')} labels={ON_WRITE_SCAN_LABELS} - checked={policy.windows.malware.on_write_scan ?? true} + checked={policy.windows.malware.on_write_scan} adjustSubfeatureOnProtectionSwitch={adjustOnWriteSettingsOnProtectionSwitch} /> diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/protection_updates/protection_updates_layout.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/protection_updates/protection_updates_layout.tsx index 5d244884c1068..7c9b637a8b8c8 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/protection_updates/protection_updates_layout.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/protection_updates/protection_updates_layout.tsx @@ -72,7 +72,6 @@ export const ProtectionUpdatesLayout = React.memo( const policy = _policy as PolicyData; const deployedVersion = policy.inputs[0].config.policy.value.global_manifest_version; - const [manifestVersion, setManifestVersion] = useState(deployedVersion); const today = moment.utc(); const defaultDate = today.clone().subtract(1, 'days'); @@ -93,27 +92,47 @@ export const ProtectionUpdatesLayout = React.memo( } }, [fetchedNote, getNoteInProgress]); - const automaticUpdatesEnabled = manifestVersion === 'latest'; const internalDateFormat = 'YYYY-MM-DD'; const displayDateFormat = 'MMMM DD, YYYY'; const formattedDate = moment.utc(deployedVersion, internalDateFormat).format(displayDateFormat); const cutoffDate = getControlledArtifactCutoffDate(); // Earliest selectable date + const [selectedManifestVersion, setSelectedManifestVersion] = useState( + deployedVersion === 'latest' ? 'latest' : selectedDate.format(internalDateFormat) + ); + const automaticUpdatesEnabled = selectedManifestVersion === 'latest'; + const viewModeSwitchLabel = automaticUpdatesEnabled ? AUTOMATIC_UPDATES_CHECKBOX_LABEL : AUTOMATIC_UPDATES_OFF_CHECKBOX_LABEL; const saveButtonEnabled = (fetchedNote ? note !== fetchedNote.note : note !== '') || - manifestVersion !== deployedVersion; + selectedManifestVersion !== deployedVersion; useEffect(() => { + // Prevent unsaved changes modal from showing when the user has not made any changes + if ( + selectedDate.isSame(defaultDate.toISOString(), 'day') && + deployedVersion !== 'latest' && + !automaticUpdatesEnabled && + !moment.utc(deployedVersion, internalDateFormat).isSame(selectedDate.toISOString(), 'days') + ) { + return; + } setUnsavedChanges(saveButtonEnabled); - }, [saveButtonEnabled, setUnsavedChanges]); + }, [ + automaticUpdatesEnabled, + defaultDate, + deployedVersion, + saveButtonEnabled, + selectedDate, + setUnsavedChanges, + ]); const onSave = useCallback(() => { const update = cloneDeep(policy); - update.inputs[0].config.policy.value.global_manifest_version = manifestVersion; + update.inputs[0].config.policy.value.global_manifest_version = selectedManifestVersion; sendPolicyUpdate({ policy: update }) .then(({ item: policyItem }) => { toasts.addSuccess({ @@ -173,7 +192,7 @@ export const ProtectionUpdatesLayout = React.memo( } }, [ policy, - manifestVersion, + selectedManifestVersion, sendPolicyUpdate, fetchedNote, note, @@ -187,13 +206,13 @@ export const ProtectionUpdatesLayout = React.memo( const { checked } = event.target; if (checked && !automaticUpdatesEnabled) { - setManifestVersion('latest'); + setSelectedManifestVersion('latest'); // Clear selected date on user enabling automatic updates if (selectedDate !== defaultDate) { setSelectedDate(defaultDate); } } else { - setManifestVersion(selectedDate.format(internalDateFormat)); + setSelectedManifestVersion(selectedDate.format(internalDateFormat)); } }, [automaticUpdatesEnabled, selectedDate, defaultDate] @@ -203,7 +222,7 @@ export const ProtectionUpdatesLayout = React.memo( (date: Moment | null) => { if (date?.isAfter(cutoffDate) && date?.isSameOrBefore(defaultDate)) { setSelectedDate(date || defaultDate); - setManifestVersion(date?.format(internalDateFormat) || 'latest'); + setSelectedManifestVersion(date?.format(internalDateFormat) || 'latest'); } }, [cutoffDate, defaultDate] diff --git a/x-pack/plugins/security_solution/public/overview/pages/data_quality.tsx b/x-pack/plugins/security_solution/public/overview/pages/data_quality.tsx index 5c74c0e334180..587cf9377c5f0 100644 --- a/x-pack/plugins/security_solution/public/overview/pages/data_quality.tsx +++ b/x-pack/plugins/security_solution/public/overview/pages/data_quality.tsx @@ -131,7 +131,7 @@ const renderOption = ( ); const DataQualityComponent: React.FC = () => { - const { hasAssistantPrivilege } = useAssistantAvailability(); + const { isAssistantEnabled } = useAssistantAvailability(); const httpFetch = KibanaServices.get().http.fetch; const { baseTheme, theme } = useThemes(); const toasts = useToasts(); @@ -284,7 +284,7 @@ const DataQualityComponent: React.FC = () => { reportDataQualityIndexChecked={reportDataQualityIndexChecked} httpFetch={httpFetch} ilmPhases={ilmPhases} - isAssistantEnabled={hasAssistantPrivilege} + isAssistantEnabled={isAssistantEnabled} isILMAvailable={isILMAvailable} lastChecked={lastChecked} openCreateCaseFlyout={openCreateCaseFlyout} diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx index 20b6341db6d91..33faddd5bea48 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx @@ -80,7 +80,7 @@ const EventDetailsPanelComponent: React.FC = ({ scopeId, isReadOnly, }) => { - const { hasAssistantPrivilege } = useAssistantAvailability(); + const { hasAssistantPrivilege, isAssistantEnabled } = useAssistantAvailability(); // TODO: changing feature flags requires a hard refresh to take effect, but this temporary workaround technically violates the rules of hooks: const useAssistant = hasAssistantPrivilege ? useAssistantOverlay : useAssistantNoop; const currentSpaceId = useSpaceId(); @@ -122,7 +122,8 @@ const EventDetailsPanelComponent: React.FC = ({ isAlert ? PROMPT_CONTEXTS[PROMPT_CONTEXT_ALERT_CATEGORY].suggestedUserPrompt : PROMPT_CONTEXTS[PROMPT_CONTEXT_EVENT_CATEGORY].suggestedUserPrompt, - isAlert ? ALERT_SUMMARY_VIEW_CONTEXT_TOOLTIP : EVENT_SUMMARY_VIEW_CONTEXT_TOOLTIP + isAlert ? ALERT_SUMMARY_VIEW_CONTEXT_TOOLTIP : EVENT_SUMMARY_VIEW_CONTEXT_TOOLTIP, + isAssistantEnabled ); const header = useMemo( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/data_table/__snapshots__/custom_timeline_data_grid_body.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/data_table/__snapshots__/custom_timeline_data_grid_body.test.tsx.snap index 3845ad9801abd..2c8c9d593b775 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/data_table/__snapshots__/custom_timeline_data_grid_body.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/data_table/__snapshots__/custom_timeline_data_grid_body.test.tsx.snap @@ -212,7 +212,7 @@ exports[`CustomTimelineDataGridBody should render exactly as snapshots 1`] = ` test added a note

note diff --git a/x-pack/plugins/security_solution/scripts/endpoint/agent_policy_generator/index.ts b/x-pack/plugins/security_solution/scripts/endpoint/agent_policy_generator/index.ts index 104bf337be5d3..afe370e1bda72 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/agent_policy_generator/index.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/agent_policy_generator/index.ts @@ -59,7 +59,6 @@ const agentPolicyGenerator: RunFn = async ({ flags, log }) => { username: flags.username as string, password: flags.password as string, apiKey: flags.apikey as string, - noCertForSsl: true, log, }); diff --git a/x-pack/plugins/security_solution/scripts/endpoint/api_emulator/index.ts b/x-pack/plugins/security_solution/scripts/endpoint/api_emulator/index.ts index 15a48654f2963..8c4d0eae4e5e7 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/api_emulator/index.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/api_emulator/index.ts @@ -79,7 +79,6 @@ const cliRunner: RunFn = async (cliContext) => { elasticsearchUrl, asSuperuser, log, - noCertForSsl: true, }); const coreServices: ExternalEdrServerEmulatorCoreServices = { diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_server/fleet_server_services.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_server/fleet_server_services.ts index ae518420531fd..1eefe220c0d39 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_server/fleet_server_services.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_server/fleet_server_services.ts @@ -10,6 +10,7 @@ import type { KbnClient } from '@kbn/test'; import execa from 'execa'; import chalk from 'chalk'; import assert from 'assert'; +import pRetry from 'p-retry'; import type { AgentPolicy, CreateAgentPolicyResponse, Output } from '@kbn/fleet-plugin/common'; import { AGENT_POLICY_API_ROUTES, @@ -137,7 +138,12 @@ export const startFleetServer = async ({ const isServerless = await isServerlessKibanaFlavor(kbnClient); const policyId = policy || !isServerless ? await getOrCreateFleetServerAgentPolicyId(kbnClient, logger) : ''; - const serviceToken = isServerless ? '' : await generateFleetServiceToken(kbnClient, logger); + const serviceToken = isServerless + ? '' + : await pRetry(async () => generateFleetServiceToken(kbnClient, logger), { + retries: 2, + forever: false, + }); const startedFleetServer = await startFleetServerWithDocker({ kbnClient, logger, @@ -677,24 +683,32 @@ export const isFleetServerRunning = async ( const url = new URL(fleetServerUrl); url.pathname = '/api/status'; - return axios - .request({ - method: 'GET', - url: url.toString(), - responseType: 'json', - // Custom agent to ensure we don't get cert errors - httpsAgent: new https.Agent({ rejectUnauthorized: false }), - }) - .then((response) => { - log.debug(`Fleet server is up and running at [${fleetServerUrl}]. Status: `, response.data); - return true; - }) - .catch(catchAxiosErrorFormatAndThrow) - .catch((e) => { - log.debug(`Fleet server not up at [${fleetServerUrl}]`); - log.verbose(`Call to [${url.toString()}] failed with:`, e); - return false; - }); + return pRetry( + async () => { + return axios + .request({ + method: 'GET', + url: url.toString(), + responseType: 'json', + // Custom agent to ensure we don't get cert errors + httpsAgent: new https.Agent({ rejectUnauthorized: false }), + }) + .then((response) => { + log.debug( + `Fleet server is up and running at [${fleetServerUrl}]. Status: `, + response.data + ); + return true; + }) + .catch(catchAxiosErrorFormatAndThrow) + .catch((e) => { + log.debug(`Fleet server not up at [${fleetServerUrl}]`); + log.verbose(`Call to [${url.toString()}] failed with:`, e); + return false; + }); + }, + { maxTimeout: 10000 } + ); }; /** diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts index 8d1d2f5e61f49..317cae2882e57 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts @@ -58,10 +58,10 @@ import type { PostAgentUnenrollResponse, CopyAgentPolicyRequest, } from '@kbn/fleet-plugin/common/types'; -import nodeFetch from 'node-fetch'; import semver from 'semver'; import axios from 'axios'; import { userInfo } from 'os'; +import pRetry from 'p-retry'; import { isFleetServerRunning } from './fleet_server/fleet_server_services'; import { getEndpointPackageInfo } from '../../../common/endpoint/utils/package'; import type { DownloadAndStoreAgentResponse } from './agent_downloads_service'; @@ -79,6 +79,7 @@ import { FleetAgentGenerator } from '../../../common/endpoint/data_generators/fl const fleetGenerator = new FleetAgentGenerator(); const CURRENT_USERNAME = userInfo().username.toLowerCase(); const DEFAULT_AGENT_POLICY_NAME = `${CURRENT_USERNAME} test policy`; + /** A Fleet agent policy that includes integrations that don't actually require an agent to run on a host. Example: SenttinelOne */ export const DEFAULT_AGENTLESS_INTEGRATIONS_AGENT_POLICY_NAME = `${CURRENT_USERNAME} - agentless integrations`; @@ -411,14 +412,20 @@ export const getAgentVersionMatchingCurrentStack = async ( ); } - const agentVersions = await axios - .get('https://artifacts-api.elastic.co/v1/versions') - .then((response) => - map( - response.data.versions.filter(isValidArtifactVersion), - (version) => version.split('-SNAPSHOT')[0] - ) - ); + const agentVersions = await pRetry( + async () => { + return axios + .get('https://artifacts-api.elastic.co/v1/versions') + .catch(catchAxiosErrorFormatAndThrow) + .then((response) => + map( + response.data.versions.filter(isValidArtifactVersion), + (version) => version.split('-SNAPSHOT')[0] + ) + ); + }, + { maxTimeout: 10000 } + ); let version = semver.maxSatisfying(agentVersions, `<=${kbnStatus.version.number}`) ?? @@ -492,16 +499,16 @@ export const getAgentDownloadUrl = async ( log?.verbose(`Retrieving elastic agent download URL from:\n ${artifactSearchUrl}`); - const searchResult: ElasticArtifactSearchResponse = await nodeFetch(artifactSearchUrl).then( - (response) => { - if (!response.ok) { - throw new Error( - `Failed to search elastic's artifact repository: ${response.statusText} (HTTP ${response.status}) {URL: ${artifactSearchUrl})` - ); - } - - return response.json(); - } + const searchResult: ElasticArtifactSearchResponse = await pRetry( + async () => { + return axios + .get(artifactSearchUrl) + .catch(catchAxiosErrorFormatAndThrow) + .then((response) => { + return response.data; + }); + }, + { maxTimeout: 10000 } ); log?.verbose(searchResult); @@ -547,16 +554,16 @@ export const getLatestAgentDownloadVersion = async ( ): Promise => { const artifactsUrl = 'https://artifacts-api.elastic.co/v1/versions'; const semverMatch = `<=${version.replace(`-SNAPSHOT`, '')}`; - const artifactVersionsResponse: { versions: string[] } = await nodeFetch(artifactsUrl).then( - (response) => { - if (!response.ok) { - throw new Error( - `Failed to retrieve list of versions from elastic's artifact repository: ${response.statusText} (HTTP ${response.status}) {URL: ${artifactsUrl})` - ); - } - - return response.json(); - } + const artifactVersionsResponse: { versions: string[] } = await pRetry( + async () => { + return axios + .get<{ versions: string[] }>(artifactsUrl) + .catch(catchAxiosErrorFormatAndThrow) + .then((response) => { + return response.data; + }); + }, + { maxTimeout: 10000 } ); const stackVersionToArtifactVersion: Record = artifactVersionsResponse.versions 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 a7dc1b7ecfe39..5d321ec09cc48 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 @@ -70,7 +70,7 @@ interface CreateRuntimeServicesOptions { log?: ToolingLog; asSuperuser?: boolean; /** If true, then a certificate will not be used when creating the Kbn/Es clients when url is `https` */ - noCertForSsl?: boolean; + useCertForSsl?: boolean; } class KbnClientExtended extends KbnClient { @@ -112,7 +112,7 @@ export const createRuntimeServices = async ({ esPassword: _esPassword, log = createToolingLogger(), asSuperuser = false, - noCertForSsl, + useCertForSsl = false, }: CreateRuntimeServicesOptions): Promise => { let username = _username; let password = _password; @@ -124,7 +124,7 @@ export const createRuntimeServices = async ({ url: kibanaUrl, username, password, - noCertForSsl, + useCertForSsl, log, }); @@ -149,7 +149,7 @@ export const createRuntimeServices = async ({ username: esUsername ?? username, password: esPassword ?? password, log, - noCertForSsl, + useCertForSsl, }) ); @@ -166,14 +166,14 @@ export const createRuntimeServices = async ({ const fleetURL = new URL(fleetServerUrl); return { - kbnClient: createKbnClient({ log, url: kibanaUrl, username, password, apiKey, noCertForSsl }), + kbnClient: createKbnClient({ log, url: kibanaUrl, username, password, apiKey, useCertForSsl }), esClient: createEsClient({ log, url: elasticsearchUrl, username: esUsername ?? username, password: esPassword ?? password, apiKey, - noCertForSsl, + useCertForSsl, }), log, localhostRealIp: getLocalhostRealIp(), @@ -222,7 +222,7 @@ export const createEsClient = ({ password, apiKey, log, - noCertForSsl, + useCertForSsl = false, }: { url: string; username: string; @@ -230,14 +230,14 @@ export const createEsClient = ({ /** If defined, both `username` and `password` will be ignored */ apiKey?: string; log?: ToolingLog; - noCertForSsl?: boolean; + useCertForSsl?: boolean; }): Client => { const isHttps = new URL(url).protocol.startsWith('https'); const clientOptions: ClientOptions = { node: buildUrlWithCredentials(url, apiKey ? '' : username, apiKey ? '' : password), }; - if (isHttps && !noCertForSsl) { + if (isHttps && useCertForSsl) { clientOptions.tls = { ca: [CA_CERTIFICATE], }; @@ -265,7 +265,7 @@ export const createKbnClient = ({ password, apiKey, log = createToolingLogger(), - noCertForSsl, + useCertForSsl = false, }: { url: string; username: string; @@ -273,7 +273,7 @@ export const createKbnClient = ({ /** If defined, both `username` and `password` will be ignored */ apiKey?: string; log?: ToolingLog; - noCertForSsl?: boolean; + useCertForSsl?: boolean; }): KbnClient => { const isHttps = new URL(url).protocol.startsWith('https'); const clientOptions: ConstructorParameters[0] = { @@ -282,7 +282,7 @@ export const createKbnClient = ({ url: buildUrlWithCredentials(url, username, password), }; - if (isHttps && !noCertForSsl) { + if (isHttps && useCertForSsl) { clientOptions.certificateAuthorities = [CA_CERTIFICATE]; } diff --git a/x-pack/plugins/security_solution/scripts/endpoint/env_data_loader/index.ts b/x-pack/plugins/security_solution/scripts/endpoint/env_data_loader/index.ts index 4de4929b8c4c2..031541e8f5403 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/env_data_loader/index.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/env_data_loader/index.ts @@ -22,7 +22,6 @@ export const cli = () => { url: cliContext.flags.kibana as string, username: cliContext.flags.username as string, password: cliContext.flags.password as string, - noCertForSsl: true, }); const options = { diff --git a/x-pack/plugins/security_solution/scripts/endpoint/sentinelone_host/common.ts b/x-pack/plugins/security_solution/scripts/endpoint/sentinelone_host/common.ts index 3bce0126fbf29..d508f9bc605bf 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/sentinelone_host/common.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/sentinelone_host/common.ts @@ -10,6 +10,7 @@ import type { AxiosRequestConfig } from 'axios'; import axios from 'axios'; import type { KbnClient } from '@kbn/test'; import { SENTINELONE_CONNECTOR_ID } from '@kbn/stack-connectors-plugin/common/sentinelone/constants'; +import pRetry from 'p-retry'; import { dump } from '../common/utils'; import { type RuleResponse } from '../../../common/api/detection_engine'; import { createToolingLogger } from '../../../common/endpoint/data_loaders/utils'; @@ -86,13 +87,18 @@ export class S1Client { this.log.debug(`Request: `, requestOptions); - return axios - .request(requestOptions) - .then((response) => { - this.log.verbose(`Response: `, response); - return response.data; - }) - .catch(catchAxiosErrorFormatAndThrow); + return pRetry( + async () => { + return axios + .request(requestOptions) + .then((response) => { + this.log.verbose(`Response: `, response); + return response.data; + }) + .catch(catchAxiosErrorFormatAndThrow); + }, + { maxTimeout: 10000 } + ); } public buildUrl(path: string): string { diff --git a/x-pack/plugins/security_solution/scripts/endpoint/sentinelone_host/index.ts b/x-pack/plugins/security_solution/scripts/endpoint/sentinelone_host/index.ts index 2c179f6e853ab..d2f4190821413 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/sentinelone_host/index.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/sentinelone_host/index.ts @@ -111,7 +111,6 @@ const runCli: RunFn = async ({ log, flags }) => { url: kibanaUrl, username, password, - noCertForSsl: true, }); const runningS1VMs = ( diff --git a/x-pack/plugins/security_solution/scripts/run_cypress/parallel.ts b/x-pack/plugins/security_solution/scripts/run_cypress/parallel.ts index e12e70cb0aa9c..08c0846d2ce8e 100644 --- a/x-pack/plugins/security_solution/scripts/run_cypress/parallel.ts +++ b/x-pack/plugins/security_solution/scripts/run_cypress/parallel.ts @@ -316,100 +316,107 @@ ${JSON.stringify( ci: process.env.CI, }; - const shutdownEs = await pRetry( - async () => - runElasticsearch({ - config, - log, - name: `ftr-${esPort}`, - esFrom: config.get('esTestCluster')?.from || 'snapshot', - onEarlyExit, - }), - { retries: 2, forever: false } - ); - - await runKibanaServer({ - procs, - config, - installDir: options?.installDir, - extraKbnOpts: - options?.installDir || options?.ci || !isOpen - ? [] - : ['--dev', '--no-dev-config', '--no-dev-credentials'], - onEarlyExit, - inspect: argv.inspect, - }); - // Setup fleet if Cypress config requires it let fleetServer: void | StartedFleetServer; - if (cypressConfigFile.env?.WITH_FLEET_SERVER) { - log.info(`Setting up fleet-server for this Cypress config`); - - const kbnClient = createKbnClient({ - url: baseUrl, - username: config.get('servers.kibana.username'), - password: config.get('servers.kibana.password'), - log, + let shutdownEs; + + try { + shutdownEs = await pRetry( + async () => + runElasticsearch({ + config, + log, + name: `ftr-${esPort}`, + esFrom: config.get('esTestCluster')?.from || 'snapshot', + onEarlyExit, + }), + { retries: 2, forever: false } + ); + + await runKibanaServer({ + procs, + config, + installDir: options?.installDir, + extraKbnOpts: + options?.installDir || options?.ci || !isOpen + ? [] + : ['--dev', '--no-dev-config', '--no-dev-credentials'], + onEarlyExit, + inspect: argv.inspect, }); - fleetServer = await startFleetServer({ - kbnClient, - logger: log, - port: - fleetServerPort ?? config.has('servers.fleetserver.port') - ? (config.get('servers.fleetserver.port') as number) - : undefined, - // `force` is needed to ensure that any currently running fleet server (perhaps left - // over from an interrupted run) is killed and a new one restarted - force: true, - }); - } + if (cypressConfigFile.env?.WITH_FLEET_SERVER) { + log.info(`Setting up fleet-server for this Cypress config`); - await providers.loadAll(); + const kbnClient = createKbnClient({ + url: baseUrl, + username: config.get('servers.kibana.username'), + password: config.get('servers.kibana.password'), + log, + }); - const functionalTestRunner = new FunctionalTestRunner( - log, - config, - EsVersion.getDefault() - ); + fleetServer = await pRetry( + async () => + startFleetServer({ + kbnClient, + logger: log, + port: + fleetServerPort ?? config.has('servers.fleetserver.port') + ? (config.get('servers.fleetserver.port') as number) + : undefined, + // `force` is needed to ensure that any currently running fleet server (perhaps left + // over from an interrupted run) is killed and a new one restarted + force: true, + }), + { retries: 2, forever: false } + ); + } - const ftrEnv = await pRetry(() => functionalTestRunner.run(abortCtrl.signal), { - retries: 1, - }); + await providers.loadAll(); - log.debug( - `Env. variables returned by [functionalTestRunner.run()]:\n`, - JSON.stringify(ftrEnv, null, 2) - ); + const functionalTestRunner = new FunctionalTestRunner( + log, + config, + EsVersion.getDefault() + ); - // Normalized the set of available env vars in cypress - const cyCustomEnv = { - ...ftrEnv, + const ftrEnv = await pRetry(() => functionalTestRunner.run(abortCtrl.signal), { + retries: 1, + }); - // NOTE: - // ELASTICSEARCH_URL needs to be created here with auth because SIEM cypress setup depends on it. At some - // points we should probably try to refactor that code to use `ELASTICSEARCH_URL_WITH_AUTH` instead - ELASTICSEARCH_URL: - ftrEnv.ELASTICSEARCH_URL ?? createUrlFromFtrConfig('elasticsearch', true), - ELASTICSEARCH_URL_WITH_AUTH: createUrlFromFtrConfig('elasticsearch', true), - ELASTICSEARCH_USERNAME: - ftrEnv.ELASTICSEARCH_USERNAME ?? config.get('servers.elasticsearch.username'), - ELASTICSEARCH_PASSWORD: - ftrEnv.ELASTICSEARCH_PASSWORD ?? config.get('servers.elasticsearch.password'), + log.debug( + `Env. variables returned by [functionalTestRunner.run()]:\n`, + JSON.stringify(ftrEnv, null, 2) + ); - FLEET_SERVER_URL: createUrlFromFtrConfig('fleetserver'), + // Normalized the set of available env vars in cypress + const cyCustomEnv = { + ...ftrEnv, - KIBANA_URL: baseUrl, - KIBANA_URL_WITH_AUTH: createUrlFromFtrConfig('kibana', true), - KIBANA_USERNAME: config.get('servers.kibana.username'), - KIBANA_PASSWORD: config.get('servers.kibana.password'), + // NOTE: + // ELASTICSEARCH_URL needs to be created here with auth because SIEM cypress setup depends on it. At some + // points we should probably try to refactor that code to use `ELASTICSEARCH_URL_WITH_AUTH` instead + ELASTICSEARCH_URL: + ftrEnv.ELASTICSEARCH_URL ?? createUrlFromFtrConfig('elasticsearch', true), + ELASTICSEARCH_URL_WITH_AUTH: createUrlFromFtrConfig('elasticsearch', true), + ELASTICSEARCH_USERNAME: + ftrEnv.ELASTICSEARCH_USERNAME ?? config.get('servers.elasticsearch.username'), + ELASTICSEARCH_PASSWORD: + ftrEnv.ELASTICSEARCH_PASSWORD ?? config.get('servers.elasticsearch.password'), - IS_SERVERLESS: config.get('serverless'), + FLEET_SERVER_URL: createUrlFromFtrConfig('fleetserver'), - ...argv.env, - }; + KIBANA_URL: baseUrl, + KIBANA_URL_WITH_AUTH: createUrlFromFtrConfig('kibana', true), + KIBANA_USERNAME: config.get('servers.kibana.username'), + KIBANA_PASSWORD: config.get('servers.kibana.password'), - log.info(` + IS_SERVERLESS: config.get('serverless'), + + ...argv.env, + }; + + log.info(` ---------------------------------------------- Cypress run ENV for file: ${filePath}: ---------------------------------------------- @@ -419,18 +426,17 @@ ${JSON.stringify(cyCustomEnv, null, 2)} ---------------------------------------------- `); - if (isOpen) { - await cypress.open({ - configFile: cypressConfigFilePath, - config: { - e2e: { - baseUrl, + if (isOpen) { + await cypress.open({ + configFile: cypressConfigFilePath, + config: { + e2e: { + baseUrl, + }, + env: cyCustomEnv, }, - env: cyCustomEnv, - }, - }); - } else { - try { + }); + } else { result = await cypress.run({ browser: 'chrome', spec: filePath, @@ -450,9 +456,9 @@ ${JSON.stringify(cyCustomEnv, null, 2)} if (!(result as CypressCommandLine.CypressRunResult)?.totalFailed) { _.pull(failedSpecFilePaths, filePath); } - } catch (error) { - result = error; } + } catch (error) { + log.error(error); } if (fleetServer) { @@ -460,7 +466,7 @@ ${JSON.stringify(cyCustomEnv, null, 2)} } await procs.stop('kibana'); - await shutdownEs(); + await shutdownEs?.(); cleanupServerPorts({ esPort, kibanaPort, fleetServerPort }); return result; diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/task.ts b/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/task.ts index 8066e6094b568..d0c4d825d57d6 100644 --- a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/task.ts +++ b/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/task.ts @@ -170,7 +170,6 @@ export class ManifestTask { this.logger.error( `unable to recover from error while attempting to retrieve last computed manifest` ); - return; } } diff --git a/x-pack/plugins/security_solution/server/endpoint/schemas/artifacts/saved_objects.ts b/x-pack/plugins/security_solution/server/endpoint/schemas/artifacts/saved_objects.ts index f0bc3a80d3406..4ca6d67031662 100644 --- a/x-pack/plugins/security_solution/server/endpoint/schemas/artifacts/saved_objects.ts +++ b/x-pack/plugins/security_solution/server/endpoint/schemas/artifacts/saved_objects.ts @@ -109,6 +109,7 @@ export const internalUnifiedManifestSchema = t.intersection([ t.type({ id: identifier, created: t.union([t.string, t.undefined]), + version: t.union([t.string, t.undefined]), }) ), ]); diff --git a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.mock.ts b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.mock.ts index a0cbc058ba6a8..31ac67b2368d6 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.mock.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.mock.ts @@ -72,6 +72,7 @@ export interface ManifestManagerMockOptions { packagePolicyService: jest.Mocked; savedObjectsClient: ReturnType; productFeaturesService: ProductFeaturesService; + experimentalFeatures?: string[]; } export const buildManifestManagerMockOptions = ( @@ -98,7 +99,8 @@ export const buildManifestManagerContextMock = ( ...fullOpts, artifactClient: createEndpointArtifactClientMock(), logger: loggingSystemMock.create().get() as jest.Mocked, - experimentalFeatures: parseExperimentalConfigValue([]).features, + experimentalFeatures: parseExperimentalConfigValue([...(opts.experimentalFeatures ?? [])]) + .features, packagerTaskPackagePolicyUpdateBatchSize: 10, esClient: elasticsearchServiceMock.createElasticsearchClient(), }; diff --git a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.test.ts b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.test.ts index 7b996c7737c89..d6e82322ffa9c 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.test.ts @@ -14,6 +14,7 @@ import type { InternalArtifactCompleteSchema, InternalArtifactSchema, InternalManifestSchema, + InternalUnifiedManifestSchema, } from '../../../schemas/artifacts'; import { createPackagePolicyWithConfigMock, @@ -32,6 +33,7 @@ import { mockFindExceptionListItemResponses, } from './manifest_manager.mock'; +import type { ManifestManagerContext } from './manifest_manager'; import { ManifestManager } from './manifest_manager'; import type { EndpointArtifactClientInterface } from '../artifact_client'; import { InvalidInternalManifestError } from '../errors'; @@ -107,6 +109,168 @@ describe('ManifestManager', () => { ARTIFACT_TRUSTED_APPS_WINDOWS = ARTIFACTS[4]; }); + describe('getLastComputedManifest from Unified Manifest SO', () => { + const mockGetAllUnifiedManifestsSOFromCache = jest.fn().mockImplementation(() => [ + { + policyId: '.global', + semanticVersion: '1.0.0', + artifactIds: [ + ARTIFACT_ID_EXCEPTIONS_MACOS, + ARTIFACT_ID_EXCEPTIONS_WINDOWS, + ARTIFACT_ID_EXCEPTIONS_LINUX, + ], + created: '20-01-2020 10:00:00.000Z', + id: '3', + }, + { + policyId: TEST_POLICY_ID_1, + semanticVersion: '1.0.0', + artifactIds: [ + ARTIFACT_ID_EXCEPTIONS_WINDOWS, + ARTIFACT_ID_TRUSTED_APPS_MACOS, + ARTIFACT_ID_TRUSTED_APPS_WINDOWS, + ], + created: '20-01-2020 10:00:00.000Z', + id: '1', + }, + { + policyId: TEST_POLICY_ID_2, + semanticVersion: '1.0.0', + artifactIds: [ARTIFACT_ID_TRUSTED_APPS_WINDOWS], + created: '20-01-2020 10:00:00.000Z', + id: '2', + }, + ]); + + test('Retrieves empty unified manifest successfully', async () => { + const savedObjectsClient = savedObjectsClientMock.create(); + const manifestManager = new ManifestManager( + buildManifestManagerContextMock({ + savedObjectsClient, + experimentalFeatures: ['unifiedManifestEnabled'], + }) + ); + + manifestManager.getAllUnifiedManifestsSO = jest.fn().mockImplementation(() => []); + + const manifest = await manifestManager.getLastComputedManifest(); + + expect(manifest?.getSchemaVersion()).toStrictEqual('v1'); + expect(manifest?.getSemanticVersion()).toStrictEqual('1.0.0'); + expect(manifest?.getSavedObjectVersion()).toStrictEqual('WzQ3NzAsMV0='); + expect(manifest?.getAllArtifacts()).toStrictEqual([]); + }); + + test('Retrieves empty unified manifest successfully but uses semanticVersion from existing legacy SO manifest', async () => { + const semanticVersion = '1.14.0'; + const savedObjectsClient = savedObjectsClientMock.create(); + const manifestManager = new ManifestManager( + buildManifestManagerContextMock({ + savedObjectsClient, + experimentalFeatures: ['unifiedManifestEnabled'], + }) + ); + + savedObjectsClient.get = jest.fn().mockImplementation(async (objectType: string) => { + if (objectType === ManifestConstants.SAVED_OBJECT_TYPE) { + return { + attributes: { + artifacts: [ + { artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: undefined }, + { artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: undefined }, + ], + semanticVersion, + }, + }; + } else { + return null; + } + }); + + manifestManager.getAllUnifiedManifestsSO = jest.fn().mockImplementation(() => []); + + const manifest = await manifestManager.getLastComputedManifest(); + + expect(manifest?.getSchemaVersion()).toStrictEqual('v1'); + expect(manifest?.getSemanticVersion()).toStrictEqual(semanticVersion); + expect(manifest?.getSavedObjectVersion()).toStrictEqual('WzQ3NzAsMV0='); + expect(manifest?.getAllArtifacts()).toStrictEqual([]); + }); + + test('Retrieves non empty manifest succesfully from Unified Saved Object', async () => { + const savedObjectsClient = savedObjectsClientMock.create(); + const manifestManagerContext = buildManifestManagerContextMock({ + savedObjectsClient, + experimentalFeatures: ['unifiedManifestEnabled'], + }); + const manifestManager = new ManifestManager(manifestManagerContext); + + ( + manifestManagerContext.artifactClient as jest.Mocked + ).fetchAll.mockReturnValue(createFetchAllArtifactsIterableMock([ARTIFACTS as Artifact[]])); + + manifestManager.getAllUnifiedManifestsSO = mockGetAllUnifiedManifestsSOFromCache; + + const manifest = await manifestManager.getLastComputedManifest(); + + expect(manifest?.getSchemaVersion()).toStrictEqual('v1'); + expect(manifest?.getSemanticVersion()).toStrictEqual('1.0.0'); + expect(manifest?.getSavedObjectVersion()).toStrictEqual('WzQ3NzAsMV0='); + expect(manifest?.getAllArtifacts()).toStrictEqual(ARTIFACTS.slice(0, 5)); + expect(manifest?.isDefaultArtifact(ARTIFACT_EXCEPTIONS_MACOS)).toBe(true); + expect(manifest?.getArtifactTargetPolicies(ARTIFACT_EXCEPTIONS_MACOS)).toStrictEqual( + new Set() + ); + expect(manifest?.isDefaultArtifact(ARTIFACT_EXCEPTIONS_WINDOWS)).toBe(true); + expect(manifest?.getArtifactTargetPolicies(ARTIFACT_EXCEPTIONS_WINDOWS)).toStrictEqual( + new Set([TEST_POLICY_ID_1]) + ); + expect(manifest?.isDefaultArtifact(ARTIFACT_TRUSTED_APPS_MACOS)).toBe(false); + expect(manifest?.getArtifactTargetPolicies(ARTIFACT_TRUSTED_APPS_MACOS)).toStrictEqual( + new Set([TEST_POLICY_ID_1]) + ); + expect(manifest?.isDefaultArtifact(ARTIFACT_TRUSTED_APPS_WINDOWS)).toBe(false); + expect(manifest?.getArtifactTargetPolicies(ARTIFACT_TRUSTED_APPS_WINDOWS)).toStrictEqual( + new Set([TEST_POLICY_ID_1, TEST_POLICY_ID_2]) + ); + }); + + test("Retrieve non empty unified manifest and skips over artifacts that can't be found", async () => { + const savedObjectsClient = savedObjectsClientMock.create(); + const manifestManagerContext = buildManifestManagerContextMock({ + savedObjectsClient, + experimentalFeatures: ['unifiedManifestEnabled'], + }); + const manifestManager = new ManifestManager(manifestManagerContext); + + manifestManager.getAllUnifiedManifestsSO = mockGetAllUnifiedManifestsSOFromCache; + + ( + manifestManagerContext.artifactClient as jest.Mocked + ).fetchAll.mockReturnValue( + createFetchAllArtifactsIterableMock([ + // report the MACOS Exceptions artifact as not found + [ + ARTIFACT_TRUSTED_APPS_MACOS, + ARTIFACT_EXCEPTIONS_WINDOWS, + ARTIFACT_TRUSTED_APPS_WINDOWS, + ARTIFACTS_BY_ID[ARTIFACT_ID_EXCEPTIONS_LINUX], + ] as Artifact[], + ]) + ); + + const manifest = await manifestManager.getLastComputedManifest(); + + expect(manifest?.getAllArtifacts()).toStrictEqual(ARTIFACTS.slice(1, 5)); + + expect(manifestManagerContext.logger.warn).toHaveBeenCalledWith( + "Missing artifacts detected! Internal artifact manifest (SavedObject version [WzQ3NzAsMV0=]) references [1] artifact IDs that don't exist.\n" + + "First 10 below (run with logging set to 'debug' to see all):\n" + + 'endpoint-exceptionlist-macos-v1-96b76a1a911662053a1562ac14c4ff1e87c2ff550d6fe52e1e0b3790526597d3' + ); + }); + }); + describe('getLastComputedManifest', () => { test('Returns null when saved object not found', async () => { const savedObjectsClient = savedObjectsClientMock.create(); @@ -282,7 +446,197 @@ describe('ManifestManager', () => { }); }); - describe('buildNewManifest', () => { + describe('commit unified manifest', () => { + test('Correctly updates, creates and deletes unified manifest so', async () => { + const context = buildManifestManagerContextMock({ + experimentalFeatures: ['unifiedManifestEnabled'], + }); + const manifestManager = new ManifestManager(context); + const manifest = ManifestManager.createDefaultManifest(); + + manifest.addEntry(ARTIFACT_EXCEPTIONS_MACOS); + manifest.addEntry(ARTIFACT_EXCEPTIONS_MACOS, TEST_POLICY_ID_1); + manifest.addEntry(ARTIFACT_TRUSTED_APPS_WINDOWS, TEST_POLICY_ID_1); + manifest.addEntry(ARTIFACT_EXCEPTIONS_WINDOWS, TEST_POLICY_ID_2); + manifest.addEntry(ARTIFACT_TRUSTED_APPS_MACOS, TEST_POLICY_ID_1); + manifest.addEntry(ARTIFACT_TRUSTED_APPS_MACOS, TEST_POLICY_ID_2); + + manifestManager.getAllUnifiedManifestsSO = jest.fn().mockImplementation(() => [ + { + policyId: '.global', + semanticVersion: '1.0.0', + artifactIds: [ARTIFACT_ID_EXCEPTIONS_MACOS], + created: '20-01-2020 10:00:00.000Z', + id: '2', + }, + { + policyId: TEST_POLICY_ID_1, + semanticVersion: '1.0.0', + artifactIds: [ARTIFACT_ID_EXCEPTIONS_MACOS, ARTIFACT_ID_TRUSTED_APPS_MACOS], + created: '20-01-2020 10:00:00.000Z', + id: '3', + }, + { + policyId: 'non-existent-policy', + semanticVersion: '1.0.0', + artifactIds: [ARTIFACT_ID_EXCEPTIONS_WINDOWS], + created: '20-01-2020 10:00:00.000Z', + id: '4', + }, + ]); + + context.savedObjectsClient.bulkCreate = jest.fn(); + context.savedObjectsClient.bulkUpdate = jest.fn(); + context.savedObjectsClient.bulkDelete = jest.fn(); + manifestManager.bumpGlobalUnifiedManifestVersion = jest.fn(); + + await expect(manifestManager.commit(manifest)).resolves.toBeUndefined(); + expect(context.savedObjectsClient.bulkCreate).toHaveBeenCalledTimes(1); + // TEST_POLICY_ID_1 and .global exists, shouldn't be created + expect(context.savedObjectsClient.bulkCreate).toHaveBeenCalledWith( + [ + { + attributes: { + artifactIds: [ARTIFACT_ID_EXCEPTIONS_WINDOWS, ARTIFACT_ID_TRUSTED_APPS_MACOS], + id: undefined, + policyId: TEST_POLICY_ID_2, + semanticVersion: '1.0.0', + }, + type: ManifestConstants.UNIFIED_SAVED_OBJECT_TYPE, + }, + ], + { initialNamespaces: ['*'] } + ); + + expect(context.savedObjectsClient.bulkUpdate).toHaveBeenCalledTimes(1); + // TEST_POLICY_ID_1 is updated, global is not due to no changes + expect(context.savedObjectsClient.bulkUpdate).toHaveBeenCalledWith([ + { + attributes: { + artifactIds: [ + ARTIFACT_ID_EXCEPTIONS_MACOS, + ARTIFACT_ID_TRUSTED_APPS_WINDOWS, + ARTIFACT_ID_TRUSTED_APPS_MACOS, + ], + policyId: TEST_POLICY_ID_1, + semanticVersion: '1.0.1', + }, + id: '3', + type: ManifestConstants.UNIFIED_SAVED_OBJECT_TYPE, + }, + ]); + + // non-existent-policy should be deleted for not being in the manifest + expect(context.savedObjectsClient.bulkDelete).toHaveBeenCalledTimes(1); + expect(context.savedObjectsClient.bulkDelete).toHaveBeenCalledWith([ + { id: '4', type: ManifestConstants.UNIFIED_SAVED_OBJECT_TYPE }, + ]); + // Global manifest wasn't updated, manual bump is required + expect(manifestManager.bumpGlobalUnifiedManifestVersion).toHaveBeenCalledTimes(1); + }); + }); + + describe('commit', () => { + test('Creates new saved object if no saved object version', async () => { + const context = buildManifestManagerContextMock({}); + const manifestManager = new ManifestManager(context); + const manifest = ManifestManager.createDefaultManifest(); + + manifest.addEntry(ARTIFACT_EXCEPTIONS_MACOS); + manifest.addEntry(ARTIFACT_EXCEPTIONS_MACOS, TEST_POLICY_ID_1); + manifest.addEntry(ARTIFACT_EXCEPTIONS_WINDOWS, TEST_POLICY_ID_2); + manifest.addEntry(ARTIFACT_TRUSTED_APPS_MACOS, TEST_POLICY_ID_1); + manifest.addEntry(ARTIFACT_TRUSTED_APPS_MACOS, TEST_POLICY_ID_2); + + context.savedObjectsClient.create = jest + .fn() + .mockImplementation((_type: string, object: InternalManifestSchema) => object); + + await expect(manifestManager.commit(manifest)).resolves.toBeUndefined(); + + expect(context.savedObjectsClient.create).toHaveBeenCalledTimes(1); + expect(context.savedObjectsClient.create).toHaveBeenNthCalledWith( + 1, + ManifestConstants.SAVED_OBJECT_TYPE, + { + artifacts: [ + { artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: undefined }, + { artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: TEST_POLICY_ID_1 }, + { artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_1 }, + { artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: TEST_POLICY_ID_2 }, + { artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_2 }, + ], + schemaVersion: 'v1', + semanticVersion: '1.0.0', + created: expect.anything(), + }, + { id: 'endpoint-manifest-v1' } + ); + }); + + test('Updates existing saved object if has saved object version', async () => { + const context = buildManifestManagerContextMock({}); + const manifestManager = new ManifestManager(context); + const manifest = new Manifest({ soVersion: '1.0.0' }); + + manifest.addEntry(ARTIFACT_EXCEPTIONS_MACOS); + manifest.addEntry(ARTIFACT_EXCEPTIONS_MACOS, TEST_POLICY_ID_1); + manifest.addEntry(ARTIFACT_EXCEPTIONS_WINDOWS, TEST_POLICY_ID_2); + manifest.addEntry(ARTIFACT_TRUSTED_APPS_MACOS, TEST_POLICY_ID_1); + manifest.addEntry(ARTIFACT_TRUSTED_APPS_MACOS, TEST_POLICY_ID_2); + + context.savedObjectsClient.update = jest + .fn() + .mockImplementation((_type: string, _id: string, object: InternalManifestSchema) => object); + + await expect(manifestManager.commit(manifest)).resolves.toBeUndefined(); + + expect(context.savedObjectsClient.update).toHaveBeenCalledTimes(1); + expect(context.savedObjectsClient.update).toHaveBeenNthCalledWith( + 1, + ManifestConstants.SAVED_OBJECT_TYPE, + 'endpoint-manifest-v1', + { + artifacts: [ + { artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: undefined }, + { artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: TEST_POLICY_ID_1 }, + { artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_1 }, + { artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: TEST_POLICY_ID_2 }, + { artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_2 }, + ], + schemaVersion: 'v1', + semanticVersion: '1.0.0', + }, + { version: '1.0.0' } + ); + }); + + test('Throws error when saved objects client fails', async () => { + const context = buildManifestManagerContextMock({}); + const manifestManager = new ManifestManager(context); + const manifest = new Manifest({ soVersion: '1.0.0' }); + const error = new Error(); + + context.savedObjectsClient.update = jest.fn().mockRejectedValue(error); + + await expect(manifestManager.commit(manifest)).rejects.toBe(error); + + expect(context.savedObjectsClient.update).toHaveBeenCalledTimes(1); + expect(context.savedObjectsClient.update).toHaveBeenNthCalledWith( + 1, + ManifestConstants.SAVED_OBJECT_TYPE, + 'endpoint-manifest-v1', + { + artifacts: [], + schemaVersion: 'v1', + semanticVersion: '1.0.0', + }, + { version: '1.0.0' } + ); + }); + }); + + describe.each([true, false])('buildNewManifest', (unifiedManifestSO) => { const SUPPORTED_ARTIFACT_NAMES = [ ARTIFACT_NAME_EXCEPTIONS_MACOS, ARTIFACT_NAME_EXCEPTIONS_WINDOWS, @@ -305,8 +659,10 @@ describe('ManifestManager', () => { ...new Set(artifacts.map((artifact) => artifact.identifier)).values(), ]; - test('Fails when exception list client fails', async () => { - const context = buildManifestManagerContextMock({}); + test(`Fails when exception list client fails when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => { + const context = buildManifestManagerContextMock({ + ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const manifestManager = new ManifestManager(context); context.exceptionListClient.findExceptionListItem = jest.fn().mockRejectedValue(new Error()); @@ -314,8 +670,10 @@ describe('ManifestManager', () => { await expect(manifestManager.buildNewManifest()).rejects.toThrow(); }); - test('Builds fully new manifest if no baseline parameter passed and no exception list items', async () => { - const context = buildManifestManagerContextMock({}); + test(`Builds fully new manifest if no baseline parameter passed and no exception list items when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => { + const context = buildManifestManagerContextMock({ + ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const manifestManager = new ManifestManager(context); context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({}); @@ -323,11 +681,6 @@ describe('ManifestManager', () => { TEST_POLICY_ID_1, ]); - context.savedObjectsClient.create = jest - .fn() - .mockImplementation((_type: string, object: InternalManifestSchema) => ({ - attributes: object, - })); const manifest = await manifestManager.buildNewManifest(); expect(manifest?.getSchemaVersion()).toStrictEqual('v1'); @@ -348,7 +701,7 @@ describe('ManifestManager', () => { } }); - test('Builds fully new manifest if no baseline parameter passed and present exception list items', async () => { + test(`Builds fully new manifest if no baseline parameter passed and present exception list items when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => { const exceptionListItem = getExceptionListItemSchemaMock({ os_types: ['macos'] }); const trustedAppListItem = getExceptionListItemSchemaMock({ os_types: ['linux'], @@ -366,7 +719,9 @@ describe('ManifestManager', () => { os_types: ['macos'], tags: ['policy:all'], }); - const context = buildManifestManagerContextMock({}); + const context = buildManifestManagerContextMock({ + ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const manifestManager = new ManifestManager(context); context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({ @@ -378,11 +733,7 @@ describe('ManifestManager', () => { }, [ENDPOINT_ARTIFACT_LISTS.blocklists.id]: { linux: [blocklistsListItem] }, }); - context.savedObjectsClient.create = jest - .fn() - .mockImplementation((_type: string, object: InternalManifestSchema) => ({ - attributes: object, - })); + context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ TEST_POLICY_ID_1, ]); @@ -432,7 +783,7 @@ describe('ManifestManager', () => { } }); - test('Reuses artifacts when baseline parameter passed and present exception list items', async () => { + test(`Reuses artifacts when baseline parameter passed and present exception list items when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => { const exceptionListItem = getExceptionListItemSchemaMock({ os_types: ['macos'] }); const trustedAppListItem = getExceptionListItemSchemaMock({ os_types: ['linux'], @@ -450,7 +801,9 @@ describe('ManifestManager', () => { os_types: ['macos'], tags: ['policy:all'], }); - const context = buildManifestManagerContextMock({}); + const context = buildManifestManagerContextMock({ + ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const manifestManager = new ManifestManager(context); context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({ @@ -459,11 +812,7 @@ describe('ManifestManager', () => { context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ TEST_POLICY_ID_1, ]); - context.savedObjectsClient.create = jest - .fn() - .mockImplementation((_type: string, object: InternalManifestSchema) => ({ - attributes: object, - })); + const oldManifest = await manifestManager.buildNewManifest(); context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({ @@ -519,7 +868,7 @@ describe('ManifestManager', () => { } }); - test('Builds fully new manifest with single entries when they are duplicated', async () => { + test(`Builds fully new manifest with single entries when they are duplicated when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => { const exceptionListItem = getExceptionListItemSchemaMock({ os_types: ['macos'] }); const trustedAppListItem = getExceptionListItemSchemaMock({ os_types: ['linux'], @@ -537,7 +886,9 @@ describe('ManifestManager', () => { os_types: ['macos'], tags: ['policy:all'], }); - const context = buildManifestManagerContextMock({}); + const context = buildManifestManagerContextMock({ + ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const manifestManager = new ManifestManager(context); const duplicatedEventFilterInDifferentPolicy = { @@ -569,11 +920,7 @@ describe('ManifestManager', () => { macos: [blocklistsListItem, blocklistsListItem], }, }); - context.savedObjectsClient.create = jest - .fn() - .mockImplementation((_type: string, object: InternalManifestSchema) => ({ - attributes: object, - })); + context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ TEST_POLICY_ID_1, TEST_POLICY_ID_2, @@ -655,7 +1002,7 @@ describe('ManifestManager', () => { } }); - test('Builds manifest with policy specific exception list items for trusted apps', async () => { + test(`Builds manifest with policy specific exception list items for trusted apps when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => { const exceptionListItem = getExceptionListItemSchemaMock({ os_types: ['macos'] }); const trustedAppListItem = getExceptionListItemSchemaMock({ os_types: ['linux'], @@ -668,7 +1015,9 @@ describe('ManifestManager', () => { ], tags: [`policy:${TEST_POLICY_ID_2}`], }); - const context = buildManifestManagerContextMock({}); + const context = buildManifestManagerContextMock({ + ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const manifestManager = new ManifestManager(context); context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({ @@ -682,12 +1031,6 @@ describe('ManifestManager', () => { TEST_POLICY_ID_2, ]); - context.savedObjectsClient.create = jest - .fn() - .mockImplementation((_type: string, object: InternalManifestSchema) => ({ - attributes: object, - })); - const manifest = await manifestManager.buildNewManifest(); expect(manifest?.getSchemaVersion()).toStrictEqual('v1'); @@ -733,7 +1076,7 @@ describe('ManifestManager', () => { }); }); - describe('buildNewManifest when using app features', () => { + describe.each([true, false])('buildNewManifest when using app features', (unifiedManifestSO) => { const SUPPORTED_ARTIFACT_NAMES = [ ARTIFACT_NAME_EXCEPTIONS_MACOS, ARTIFACT_NAME_EXCEPTIONS_WINDOWS, @@ -756,7 +1099,7 @@ describe('ManifestManager', () => { ...new Set(artifacts.map((artifact) => artifact.identifier)).values(), ]; - test('when it has endpoint artifact management app feature it should not generate host isolation exceptions', async () => { + test(`when it has endpoint artifact management app feature it should not generate host isolation exceptions when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => { const exceptionListItem = getExceptionListItemSchemaMock({ os_types: ['macos'] }); const trustedAppListItem = getExceptionListItemSchemaMock({ os_types: ['linux'], @@ -774,9 +1117,10 @@ describe('ManifestManager', () => { os_types: ['macos'], tags: ['policy:all'], }); - const context = buildManifestManagerContextMock({}, [ - ProductFeatureSecurityKey.endpointArtifactManagement, - ]); + const context = buildManifestManagerContextMock( + { ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}) }, + [ProductFeatureSecurityKey.endpointArtifactManagement] + ); const manifestManager = new ManifestManager(context); context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({ @@ -788,11 +1132,7 @@ describe('ManifestManager', () => { }, [ENDPOINT_ARTIFACT_LISTS.blocklists.id]: { linux: [blocklistsListItem] }, }); - context.savedObjectsClient.create = jest - .fn() - .mockImplementation((_type: string, object: InternalManifestSchema) => ({ - attributes: object, - })); + context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ TEST_POLICY_ID_1, ]); @@ -840,7 +1180,7 @@ describe('ManifestManager', () => { } }); - test('when it has endpoint artifact management and response actions app features it should generate all exceptions', async () => { + test(`when it has endpoint artifact management and response actions app features it should generate all exceptions when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => { const exceptionListItem = getExceptionListItemSchemaMock({ os_types: ['macos'] }); const trustedAppListItem = getExceptionListItemSchemaMock({ os_types: ['linux'], @@ -858,10 +1198,13 @@ describe('ManifestManager', () => { os_types: ['macos'], tags: ['policy:all'], }); - const context = buildManifestManagerContextMock({}, [ - ProductFeatureSecurityKey.endpointArtifactManagement, - ProductFeatureSecurityKey.endpointResponseActions, - ]); + const context = buildManifestManagerContextMock( + { ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}) }, + [ + ProductFeatureSecurityKey.endpointArtifactManagement, + ProductFeatureSecurityKey.endpointResponseActions, + ] + ); const manifestManager = new ManifestManager(context); context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({ @@ -873,11 +1216,7 @@ describe('ManifestManager', () => { }, [ENDPOINT_ARTIFACT_LISTS.blocklists.id]: { linux: [blocklistsListItem] }, }); - context.savedObjectsClient.create = jest - .fn() - .mockImplementation((_type: string, object: InternalManifestSchema) => ({ - attributes: object, - })); + context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ TEST_POLICY_ID_1, ]); @@ -927,7 +1266,7 @@ describe('ManifestManager', () => { } }); - test('when does not have right app features, should not generate any exception', async () => { + test(`when does not have right app features, should not generate any exception when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => { const exceptionListItem = getExceptionListItemSchemaMock({ os_types: ['macos'] }); const trustedAppListItem = getExceptionListItemSchemaMock({ os_types: ['linux'], @@ -945,7 +1284,10 @@ describe('ManifestManager', () => { os_types: ['macos'], tags: ['policy:all'], }); - const context = buildManifestManagerContextMock({}, []); + const context = buildManifestManagerContextMock( + { ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}) }, + [] + ); const manifestManager = new ManifestManager(context); context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({ @@ -957,123 +1299,7 @@ describe('ManifestManager', () => { }, [ENDPOINT_ARTIFACT_LISTS.blocklists.id]: { linux: [blocklistsListItem] }, }); - context.savedObjectsClient.create = jest - .fn() - .mockImplementation((_type: string, object: InternalManifestSchema) => ({ - attributes: object, - })); - context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ - TEST_POLICY_ID_1, - ]); - - const manifest = await manifestManager.buildNewManifest(); - - expect(manifest?.getSchemaVersion()).toStrictEqual('v1'); - expect(manifest?.getSemanticVersion()).toStrictEqual('1.0.0'); - expect(manifest?.getSavedObjectVersion()).toBeUndefined(); - - const artifacts = manifest.getAllArtifacts(); - - expect(artifacts.length).toBe(15); - expect(getArtifactIds(artifacts)).toStrictEqual(SUPPORTED_ARTIFACT_NAMES); - - expect(getArtifactObject(artifacts[0])).toStrictEqual({ entries: [] }); - expect(getArtifactObject(artifacts[1])).toStrictEqual({ entries: [] }); - expect(getArtifactObject(artifacts[2])).toStrictEqual({ entries: [] }); - expect(getArtifactObject(artifacts[3])).toStrictEqual({ entries: [] }); - expect(getArtifactObject(artifacts[4])).toStrictEqual({ entries: [] }); - expect(getArtifactObject(artifacts[5])).toStrictEqual({ entries: [] }); - expect(getArtifactObject(artifacts[6])).toStrictEqual({ entries: [] }); - expect(getArtifactObject(artifacts[7])).toStrictEqual({ entries: [] }); - expect(getArtifactObject(artifacts[8])).toStrictEqual({ entries: [] }); - expect(getArtifactObject(artifacts[9])).toStrictEqual({ entries: [] }); - expect(getArtifactObject(artifacts[10])).toStrictEqual({ entries: [] }); - expect(getArtifactObject(artifacts[11])).toStrictEqual({ entries: [] }); - expect(getArtifactObject(artifacts[12])).toStrictEqual({ entries: [] }); - expect(getArtifactObject(artifacts[13])).toStrictEqual({ entries: [] }); - expect(getArtifactObject(artifacts[14])).toStrictEqual({ entries: [] }); - - for (const artifact of artifacts) { - expect(manifest.isDefaultArtifact(artifact)).toBe(true); - expect(manifest.getArtifactTargetPolicies(artifact)).toStrictEqual( - new Set([TEST_POLICY_ID_1]) - ); - } - }); - }); - - describe('buildNewManifest when Endpoint Exceptions contain `matches`', () => { - test('when contains only `wildcard`, `event.module=endpoint` is added', async () => { - const exceptionListItem = getExceptionListItemSchemaMock({ - os_types: ['macos'], - entries: [ - { type: 'wildcard', operator: 'included', field: 'path', value: '*match_me*' }, - { type: 'wildcard', operator: 'excluded', field: 'not_path', value: '*dont_match_me*' }, - ], - }); - const expectedExceptionListItem = getExceptionListItemSchemaMock({ - os_types: ['macos'], - entries: [ - ...exceptionListItem.entries, - { type: 'match', operator: 'included', field: 'event.module', value: 'endpoint' }, - ], - }); - - const context = buildManifestManagerContextMock({}); - const manifestManager = new ManifestManager(context); - - context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({ - [ENDPOINT_LIST_ID]: { macos: [exceptionListItem] }, - }); - context.savedObjectsClient.create = jest - .fn() - .mockImplementation((_type: string, object: InternalManifestSchema) => ({ - attributes: object, - })); - context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ - TEST_POLICY_ID_1, - ]); - - const manifest = await manifestManager.buildNewManifest(); - - expect(manifest?.getSchemaVersion()).toStrictEqual('v1'); - expect(manifest?.getSemanticVersion()).toStrictEqual('1.0.0'); - expect(manifest?.getSavedObjectVersion()).toBeUndefined(); - - const artifacts = manifest.getAllArtifacts(); - - expect(artifacts.length).toBe(15); - - expect(getArtifactObject(artifacts[0])).toStrictEqual({ - entries: translateToEndpointExceptions([expectedExceptionListItem], 'v1'), - }); - }); - - test('when contains anything next to `wildcard`, nothing is added', async () => { - const exceptionListItem = getExceptionListItemSchemaMock({ - os_types: ['macos'], - entries: [ - { type: 'wildcard', operator: 'included', field: 'path', value: '*match_me*' }, - { type: 'wildcard', operator: 'excluded', field: 'path', value: '*dont_match_me*' }, - { type: 'match', operator: 'included', field: 'path', value: 'something' }, - ], - }); - const expectedExceptionListItem = getExceptionListItemSchemaMock({ - os_types: ['macos'], - entries: [...exceptionListItem.entries], - }); - const context = buildManifestManagerContextMock({}); - const manifestManager = new ManifestManager(context); - - context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({ - [ENDPOINT_LIST_ID]: { macos: [exceptionListItem] }, - }); - context.savedObjectsClient.create = jest - .fn() - .mockImplementation((_type: string, object: InternalManifestSchema) => ({ - attributes: object, - })); context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ TEST_POLICY_ID_1, ]); @@ -1087,16 +1313,129 @@ describe('ManifestManager', () => { const artifacts = manifest.getAllArtifacts(); expect(artifacts.length).toBe(15); + expect(getArtifactIds(artifacts)).toStrictEqual(SUPPORTED_ARTIFACT_NAMES); - expect(getArtifactObject(artifacts[0])).toStrictEqual({ - entries: translateToEndpointExceptions([expectedExceptionListItem], 'v1'), - }); + expect(getArtifactObject(artifacts[0])).toStrictEqual({ entries: [] }); + expect(getArtifactObject(artifacts[1])).toStrictEqual({ entries: [] }); + expect(getArtifactObject(artifacts[2])).toStrictEqual({ entries: [] }); + expect(getArtifactObject(artifacts[3])).toStrictEqual({ entries: [] }); + expect(getArtifactObject(artifacts[4])).toStrictEqual({ entries: [] }); + expect(getArtifactObject(artifacts[5])).toStrictEqual({ entries: [] }); + expect(getArtifactObject(artifacts[6])).toStrictEqual({ entries: [] }); + expect(getArtifactObject(artifacts[7])).toStrictEqual({ entries: [] }); + expect(getArtifactObject(artifacts[8])).toStrictEqual({ entries: [] }); + expect(getArtifactObject(artifacts[9])).toStrictEqual({ entries: [] }); + expect(getArtifactObject(artifacts[10])).toStrictEqual({ entries: [] }); + expect(getArtifactObject(artifacts[11])).toStrictEqual({ entries: [] }); + expect(getArtifactObject(artifacts[12])).toStrictEqual({ entries: [] }); + expect(getArtifactObject(artifacts[13])).toStrictEqual({ entries: [] }); + expect(getArtifactObject(artifacts[14])).toStrictEqual({ entries: [] }); + + for (const artifact of artifacts) { + expect(manifest.isDefaultArtifact(artifact)).toBe(true); + expect(manifest.getArtifactTargetPolicies(artifact)).toStrictEqual( + new Set([TEST_POLICY_ID_1]) + ); + } }); }); - describe('deleteArtifacts', () => { - test('Successfully invokes saved objects client', async () => { - const context = buildManifestManagerContextMock({}); + describe.each([true, false])( + 'buildNewManifest when Endpoint Exceptions contain `matches`', + (unifiedManifestSO) => { + test(`when contains only \`wildcard\`, \`event.module=endpoint\` is added when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => { + const exceptionListItem = getExceptionListItemSchemaMock({ + os_types: ['macos'], + entries: [ + { type: 'wildcard', operator: 'included', field: 'path', value: '*match_me*' }, + { type: 'wildcard', operator: 'excluded', field: 'not_path', value: '*dont_match_me*' }, + ], + }); + const expectedExceptionListItem = getExceptionListItemSchemaMock({ + os_types: ['macos'], + entries: [ + ...exceptionListItem.entries, + { type: 'match', operator: 'included', field: 'event.module', value: 'endpoint' }, + ], + }); + + const context = buildManifestManagerContextMock({ + ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); + const manifestManager = new ManifestManager(context); + + context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({ + [ENDPOINT_LIST_ID]: { macos: [exceptionListItem] }, + }); + + context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ + TEST_POLICY_ID_1, + ]); + + const manifest = await manifestManager.buildNewManifest(); + + expect(manifest?.getSchemaVersion()).toStrictEqual('v1'); + expect(manifest?.getSemanticVersion()).toStrictEqual('1.0.0'); + expect(manifest?.getSavedObjectVersion()).toBeUndefined(); + + const artifacts = manifest.getAllArtifacts(); + + expect(artifacts.length).toBe(15); + + expect(getArtifactObject(artifacts[0])).toStrictEqual({ + entries: translateToEndpointExceptions([expectedExceptionListItem], 'v1'), + }); + }); + + test(`when contains anything next to \`wildcard\`, nothing is added when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => { + const exceptionListItem = getExceptionListItemSchemaMock({ + os_types: ['macos'], + entries: [ + { type: 'wildcard', operator: 'included', field: 'path', value: '*match_me*' }, + { type: 'wildcard', operator: 'excluded', field: 'path', value: '*dont_match_me*' }, + { type: 'match', operator: 'included', field: 'path', value: 'something' }, + ], + }); + const expectedExceptionListItem = getExceptionListItemSchemaMock({ + os_types: ['macos'], + entries: [...exceptionListItem.entries], + }); + + const context = buildManifestManagerContextMock({ + ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); + const manifestManager = new ManifestManager(context); + + context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({ + [ENDPOINT_LIST_ID]: { macos: [exceptionListItem] }, + }); + + context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ + TEST_POLICY_ID_1, + ]); + + const manifest = await manifestManager.buildNewManifest(); + + expect(manifest?.getSchemaVersion()).toStrictEqual('v1'); + expect(manifest?.getSemanticVersion()).toStrictEqual('1.0.0'); + expect(manifest?.getSavedObjectVersion()).toBeUndefined(); + + const artifacts = manifest.getAllArtifacts(); + + expect(artifacts.length).toBe(15); + + expect(getArtifactObject(artifacts[0])).toStrictEqual({ + entries: translateToEndpointExceptions([expectedExceptionListItem], 'v1'), + }); + }); + } + ); + + describe.each([true, false])('deleteArtifacts', (unifiedManifestSO) => { + test(`Successfully invokes saved objects client when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => { + const context = buildManifestManagerContextMock({ + ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const manifestManager = new ManifestManager(context); await expect( @@ -1112,8 +1451,10 @@ describe('ManifestManager', () => { ]); }); - test('Returns errors for partial failures', async () => { - const context = buildManifestManagerContextMock({}); + test(`Returns errors for partial failures when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => { + const context = buildManifestManagerContextMock({ + ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const artifactClient = context.artifactClient as jest.Mocked; const manifestManager = new ManifestManager(context); const error = new Error(); @@ -1140,9 +1481,11 @@ describe('ManifestManager', () => { }); }); - describe('pushArtifacts', () => { - test('Successfully invokes artifactClient', async () => { - const context = buildManifestManagerContextMock({}); + describe.each([true, false])('pushArtifacts', (unifiedManifestSO) => { + test(`Successfully invokes artifactClient when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => { + const context = buildManifestManagerContextMock({ + ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const artifactClient = context.artifactClient as jest.Mocked; const manifestManager = new ManifestManager(context); const newManifest = ManifestManager.createDefaultManifest(); @@ -1164,8 +1507,10 @@ describe('ManifestManager', () => { ]); }); - test('Returns errors for partial failures', async () => { - const context = buildManifestManagerContextMock({}); + test(`Returns errors for partial failures when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => { + const context = buildManifestManagerContextMock({ + ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const artifactClient = context.artifactClient as jest.Mocked; const manifestManager = new ManifestManager(context); const newManifest = ManifestManager.createDefaultManifest(); @@ -1206,114 +1551,16 @@ describe('ManifestManager', () => { }); }); - describe('commit', () => { - test('Creates new saved object if no saved object version', async () => { - const context = buildManifestManagerContextMock({}); - const manifestManager = new ManifestManager(context); - const manifest = ManifestManager.createDefaultManifest(); - - manifest.addEntry(ARTIFACT_EXCEPTIONS_MACOS); - manifest.addEntry(ARTIFACT_EXCEPTIONS_MACOS, TEST_POLICY_ID_1); - manifest.addEntry(ARTIFACT_EXCEPTIONS_WINDOWS, TEST_POLICY_ID_2); - manifest.addEntry(ARTIFACT_TRUSTED_APPS_MACOS, TEST_POLICY_ID_1); - manifest.addEntry(ARTIFACT_TRUSTED_APPS_MACOS, TEST_POLICY_ID_2); - - context.savedObjectsClient.create = jest - .fn() - .mockImplementation((_type: string, object: InternalManifestSchema) => object); - - await expect(manifestManager.commit(manifest)).resolves.toBeUndefined(); - - expect(context.savedObjectsClient.create).toHaveBeenCalledTimes(1); - expect(context.savedObjectsClient.create).toHaveBeenNthCalledWith( - 1, - ManifestConstants.SAVED_OBJECT_TYPE, - { - artifacts: [ - { artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: undefined }, - { artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: TEST_POLICY_ID_1 }, - { artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_1 }, - { artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: TEST_POLICY_ID_2 }, - { artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_2 }, - ], - schemaVersion: 'v1', - semanticVersion: '1.0.0', - created: expect.anything(), - }, - { id: 'endpoint-manifest-v1' } - ); - }); - - test('Updates existing saved object if has saved object version', async () => { - const context = buildManifestManagerContextMock({}); - const manifestManager = new ManifestManager(context); - const manifest = new Manifest({ soVersion: '1.0.0' }); - - manifest.addEntry(ARTIFACT_EXCEPTIONS_MACOS); - manifest.addEntry(ARTIFACT_EXCEPTIONS_MACOS, TEST_POLICY_ID_1); - manifest.addEntry(ARTIFACT_EXCEPTIONS_WINDOWS, TEST_POLICY_ID_2); - manifest.addEntry(ARTIFACT_TRUSTED_APPS_MACOS, TEST_POLICY_ID_1); - manifest.addEntry(ARTIFACT_TRUSTED_APPS_MACOS, TEST_POLICY_ID_2); - - context.savedObjectsClient.update = jest - .fn() - .mockImplementation((_type: string, _id: string, object: InternalManifestSchema) => object); - - await expect(manifestManager.commit(manifest)).resolves.toBeUndefined(); - - expect(context.savedObjectsClient.update).toHaveBeenCalledTimes(1); - expect(context.savedObjectsClient.update).toHaveBeenNthCalledWith( - 1, - ManifestConstants.SAVED_OBJECT_TYPE, - 'endpoint-manifest-v1', - { - artifacts: [ - { artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: undefined }, - { artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: TEST_POLICY_ID_1 }, - { artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_1 }, - { artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: TEST_POLICY_ID_2 }, - { artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_2 }, - ], - schemaVersion: 'v1', - semanticVersion: '1.0.0', - }, - { version: '1.0.0' } - ); - }); - - test('Throws error when saved objects client fails', async () => { - const context = buildManifestManagerContextMock({}); - const manifestManager = new ManifestManager(context); - const manifest = new Manifest({ soVersion: '1.0.0' }); - const error = new Error(); - - context.savedObjectsClient.update = jest.fn().mockRejectedValue(error); - - await expect(manifestManager.commit(manifest)).rejects.toBe(error); - - expect(context.savedObjectsClient.update).toHaveBeenCalledTimes(1); - expect(context.savedObjectsClient.update).toHaveBeenNthCalledWith( - 1, - ManifestConstants.SAVED_OBJECT_TYPE, - 'endpoint-manifest-v1', - { - artifacts: [], - schemaVersion: 'v1', - semanticVersion: '1.0.0', - }, - { version: '1.0.0' } - ); - }); - }); - - describe('tryDispatch', () => { + describe.each([true, false])('tryDispatch', (unifiedSavedObject) => { const getMockPolicyFetchAllItems = (items: PackagePolicy[]) => jest.fn(async function* () { yield items; }); - test('Should not dispatch if no policies', async () => { - const context = buildManifestManagerContextMock({}); + test(`Should not dispatch if no policies when unifiedManifestEnabled feature flag is set to: ${unifiedSavedObject}`, async () => { + const context = buildManifestManagerContextMock({ + ...(unifiedSavedObject ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const manifestManager = new ManifestManager(context); const manifest = new Manifest({ soVersion: '1.0.0' }); @@ -1325,8 +1572,10 @@ describe('ManifestManager', () => { expect(context.packagePolicyService.bulkUpdate).toHaveBeenCalledTimes(0); }); - test('Should return errors if invalid config for package policy', async () => { - const context = buildManifestManagerContextMock({}); + test(`Should return errors if invalid config for package policy when unifiedManifestEnabled feature flag is set to: ${unifiedSavedObject}`, async () => { + const context = buildManifestManagerContextMock({ + ...(unifiedSavedObject ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const manifestManager = new ManifestManager(context); const manifest = new Manifest({ soVersion: '1.0.0' }); @@ -1343,8 +1592,10 @@ describe('ManifestManager', () => { expect(context.packagePolicyService.bulkUpdate).toHaveBeenCalledTimes(0); }); - test('Should not dispatch if semantic version has not changed', async () => { - const context = buildManifestManagerContextMock({}); + test(`Should not dispatch if semantic version has not changed when unifiedManifestEnabled feature flag is set to: ${unifiedSavedObject}`, async () => { + const context = buildManifestManagerContextMock({ + ...(unifiedSavedObject ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const manifestManager = new ManifestManager(context); const manifest = new Manifest({ soVersion: '1.0.0' }); @@ -1372,8 +1623,10 @@ describe('ManifestManager', () => { expect(context.packagePolicyService.bulkUpdate).toHaveBeenCalledTimes(0); }); - test('Should dispatch to only policies where list of artifacts changed', async () => { - const context = buildManifestManagerContextMock({}); + test(`Should dispatch to only policies where list of artifacts changed when unifiedManifestEnabled feature flag is set to: ${unifiedSavedObject}`, async () => { + const context = buildManifestManagerContextMock({ + ...(unifiedSavedObject ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const manifestManager = new ManifestManager(context); const manifest = new Manifest({ soVersion: '1.0.0', semanticVersion: '1.0.1' }); @@ -1441,8 +1694,10 @@ describe('ManifestManager', () => { ); }); - test('Should dispatch to only policies where artifact content changed', async () => { - const context = buildManifestManagerContextMock({}); + test(`Should dispatch to only policies where artifact content changed when unifiedManifestEnabled feature flag is set to: ${unifiedSavedObject}`, async () => { + const context = buildManifestManagerContextMock({ + ...(unifiedSavedObject ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const manifestManager = new ManifestManager(context); const manifest = new Manifest({ soVersion: '1.0.0', semanticVersion: '1.0.1' }); @@ -1512,8 +1767,10 @@ describe('ManifestManager', () => { ); }); - test('Should return partial errors', async () => { - const context = buildManifestManagerContextMock({}); + test(`Should return partial errors when unifiedManifestEnabled feature flag is set to: ${unifiedSavedObject}`, async () => { + const context = buildManifestManagerContextMock({ + ...(unifiedSavedObject ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const manifestManager = new ManifestManager(context); const error = new Error(); @@ -1556,9 +1813,11 @@ describe('ManifestManager', () => { }); }); - describe('cleanup artifacts', () => { - test('Successfully removes orphan artifacts', async () => { - const context = buildManifestManagerContextMock({}); + describe.each([true, false])('cleanup artifacts', (unifiedSavedObject) => { + test(`Successfully removes orphan artifacts when unifiedManifestEnabled feature flag is set to: ${unifiedSavedObject}`, async () => { + const context = buildManifestManagerContextMock({ + ...(unifiedSavedObject ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const manifestManager = new ManifestManager(context); (context.artifactClient.fetchAll as jest.Mock).mockReturnValue( @@ -1586,8 +1845,10 @@ describe('ManifestManager', () => { ]); }); - test('When there is no artifact to be removed', async () => { - const context = buildManifestManagerContextMock({}); + test(`When there is no artifact to be removed when unifiedManifestEnabled feature flag is set to: ${unifiedSavedObject}`, async () => { + const context = buildManifestManagerContextMock({ + ...(unifiedSavedObject ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const manifestManager = new ManifestManager(context); context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({}); @@ -1625,4 +1886,336 @@ describe('ManifestManager', () => { expect(context.artifactClient.bulkDeleteArtifacts).toHaveBeenCalledTimes(0); }); }); + + describe('Unified Manifest Methods', () => { + let manifestManager: ManifestManager; + let context: ManifestManagerContext; + + beforeEach(() => { + context = buildManifestManagerContextMock({}); + manifestManager = new ManifestManager(context); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('transforms', () => { + const createLegacyManifestSO = ( + opts: { + semanticVersion?: string; + } = {} + ) => ({ + attributes: { + schemaVersion: 'v1' as const, + semanticVersion: opts.semanticVersion ?? '1.0.0', + artifacts: [ + { artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: undefined }, + { artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: undefined }, + { artifactId: ARTIFACT_ID_EXCEPTIONS_LINUX, policyId: undefined }, + { artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: TEST_POLICY_ID_1 }, + { artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_1 }, + { artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_1 }, + { artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_2 }, + ], + }, + version: 'WzQ3NzAsMV0=', + }); + + const createUnifiedManifestSO = (globalSemanticVersion?: string) => [ + { + policyId: '.global', + semanticVersion: globalSemanticVersion ?? '1.0.0', + artifactIds: [ + ARTIFACT_ID_EXCEPTIONS_MACOS, + ARTIFACT_ID_EXCEPTIONS_WINDOWS, + ARTIFACT_ID_EXCEPTIONS_LINUX, + ], + ...(globalSemanticVersion ? { created: '20-01-2020 10:00:00.000Z' } : {}), + id: '3', + }, + { + policyId: TEST_POLICY_ID_1, + semanticVersion: '1.0.0', + artifactIds: [ + ARTIFACT_ID_EXCEPTIONS_WINDOWS, + ARTIFACT_ID_TRUSTED_APPS_MACOS, + ARTIFACT_ID_TRUSTED_APPS_WINDOWS, + ], + ...(globalSemanticVersion ? { created: '20-01-2020 10:00:00.000Z' } : {}), + id: '1', + }, + { + policyId: TEST_POLICY_ID_2, + semanticVersion: '1.0.0', + artifactIds: [ARTIFACT_ID_TRUSTED_APPS_WINDOWS], + ...(globalSemanticVersion ? { created: '20-01-2020 10:00:00.000Z' } : {}), + id: '2', + }, + ]; + + describe('transformUnifiedManifestSOtoLegacyManifestSO', () => { + const unifiedManifestSO = createUnifiedManifestSO('1.0.5'); + test('should transform unified manifest saved object to legacy manifest saved object', async () => { + const expectedLegacyManifestSO = createLegacyManifestSO({ semanticVersion: '1.0.5' }); + + expect( + manifestManager.transformUnifiedManifestSOtoLegacyManifestSO( + unifiedManifestSO as InternalUnifiedManifestSchema[] + ) + ).toEqual(expectedLegacyManifestSO); + }); + + test('should return empty artifacts array when unified manifest saved object is empty', async () => { + const emptyUnifiedManifestSO: InternalUnifiedManifestSchema[] = []; + const expectedEmptyLegacyManifestSO = createLegacyManifestSO(); + expect( + manifestManager.transformUnifiedManifestSOtoLegacyManifestSO(emptyUnifiedManifestSO) + ).toEqual({ + ...expectedEmptyLegacyManifestSO, + attributes: { ...expectedEmptyLegacyManifestSO.attributes, artifacts: [] }, + }); + }); + + test('should return empty artifacts array when unified manifest saved object is empty but semanticVersion was provided', async () => { + const semanticVersion = '1.14.0'; + const emptyUnifiedManifestSO: InternalUnifiedManifestSchema[] = []; + const expectedEmptyLegacyManifestSO = createLegacyManifestSO(); + expect( + manifestManager.transformUnifiedManifestSOtoLegacyManifestSO( + emptyUnifiedManifestSO, + semanticVersion + ) + ).toEqual({ + ...expectedEmptyLegacyManifestSO, + attributes: { + ...expectedEmptyLegacyManifestSO.attributes, + artifacts: [], + semanticVersion, + }, + }); + }); + }); + describe('transformLegacyManifestSOtoUnifiedManifestSO', () => { + const unifiedManifestSO = createUnifiedManifestSO(); + const expectedLegacyManifestSO = createLegacyManifestSO().attributes; + test('should properly transform legacy manifest to unified manifest saved object with empty exising unified manifest so', async () => { + expect( + manifestManager.transformLegacyManifestSOtoUnifiedManifestSO( + expectedLegacyManifestSO, + [] + ) + ).toEqual(unifiedManifestSO.map((item) => ({ ...item, id: undefined }))); + }); + + test('should properly transform legacy manifest to unified manifest saved object with empty exising unified manifest so and propagate semanticVersion from the manifest', async () => { + const semanticVersion = '1.14.0'; + const expectedLegacyManifestSOWithSemanticVersion = createLegacyManifestSO({ + semanticVersion, + }).attributes; + const unifiedManifestSOWithSemanticVersion = createUnifiedManifestSO(semanticVersion); + + expect( + manifestManager.transformLegacyManifestSOtoUnifiedManifestSO( + expectedLegacyManifestSOWithSemanticVersion, + [] + ) + ).toEqual( + unifiedManifestSOWithSemanticVersion.map((item) => ({ + ...item, + id: undefined, + created: undefined, + })) + ); + }); + + test('should properly transform legacy manifest to unified manifest saved object with existing unified manifest so', async () => { + const createUnifiedManifests = (empty = false) => [ + { + policyId: '.global', + semanticVersion: '1.0.2', + artifactIds: !empty + ? [ + ARTIFACT_ID_EXCEPTIONS_MACOS, + ARTIFACT_ID_EXCEPTIONS_WINDOWS, + ARTIFACT_ID_EXCEPTIONS_LINUX, + ] + : [], + id: '3', + ...(empty ? { created: '2000' } : {}), + }, + { + policyId: TEST_POLICY_ID_1, + semanticVersion: '1.0.5', + artifactIds: !empty + ? [ + ARTIFACT_ID_EXCEPTIONS_WINDOWS, + ARTIFACT_ID_TRUSTED_APPS_MACOS, + ARTIFACT_ID_TRUSTED_APPS_WINDOWS, + ] + : [], + id: '1', + ...(empty ? { created: '2000' } : {}), + }, + { + policyId: TEST_POLICY_ID_2, + semanticVersion: '1.0.1', + artifactIds: !empty ? [ARTIFACT_ID_TRUSTED_APPS_WINDOWS] : [], + id: '2', + ...(empty ? { created: '2000' } : {}), + }, + ]; + + const existingUnifiedManifest = createUnifiedManifests(true); + const output = createUnifiedManifests(); + + const legacyManifest = createLegacyManifestSO().attributes; + + expect( + manifestManager.transformLegacyManifestSOtoUnifiedManifestSO( + legacyManifest, + existingUnifiedManifest as InternalUnifiedManifestSchema[] + ) + ).toEqual(output); + }); + }); + }); + + describe('prepareUnifiedManifestsSOUpdates', () => { + const existingUnifiedManifests = ['.global', TEST_POLICY_ID_1, TEST_POLICY_ID_2].map( + (policyId, idx) => ({ + policyId, + semanticVersion: '1.0.0', + artifactIds: [ARTIFACT_ID_EXCEPTIONS_WINDOWS], + id: `${idx}`, + created: '1', + version: 'abc', + }) + ); + + const bumpSemanticVersion = ( + manifests: Array>, + semanticVersion = '1.0.1' + ) => + manifests.map((manifest) => ({ + ...manifest, + semanticVersion, + })); + + test('correctly selects manifests to create', () => { + const unifiedManifest = existingUnifiedManifests.map( + ({ id, created, ...manifest }) => manifest + ); + + const { unifiedManifestsToUpdate, unifiedManifestsToCreate, unifiedManifestsToDelete } = + manifestManager.prepareUnifiedManifestsSOUpdates(unifiedManifest, []); + + expect(unifiedManifestsToUpdate).toEqual([]); + expect(unifiedManifestsToCreate).toEqual(unifiedManifest); + expect(unifiedManifestsToDelete).toEqual([]); + }); + test('correctly selects manifests to delete', () => { + const newUnifiedManifests = existingUnifiedManifests.slice(0, 2); + + const { unifiedManifestsToUpdate, unifiedManifestsToCreate, unifiedManifestsToDelete } = + manifestManager.prepareUnifiedManifestsSOUpdates( + newUnifiedManifests, + existingUnifiedManifests + ); + + expect(unifiedManifestsToUpdate).toEqual([]); + expect(unifiedManifestsToCreate).toEqual([]); + expect(unifiedManifestsToDelete).toEqual([existingUnifiedManifests[2].id]); + }); + test('correctly selects manifests to update when artifactIds changed', () => { + const newUnifiedManifests = existingUnifiedManifests.map((manifest) => ({ + ...manifest, + artifactIds: [ARTIFACT_ID_EXCEPTIONS_WINDOWS, ARTIFACT_ID_EXCEPTIONS_WINDOWS], + })); + + const expectedUnifiedManifestsToUpdate = bumpSemanticVersion(newUnifiedManifests); + + const { unifiedManifestsToUpdate, unifiedManifestsToCreate, unifiedManifestsToDelete } = + manifestManager.prepareUnifiedManifestsSOUpdates( + newUnifiedManifests, + existingUnifiedManifests + ); + + expect(unifiedManifestsToUpdate).toEqual(expectedUnifiedManifestsToUpdate); + expect(unifiedManifestsToCreate).toEqual([]); + expect(unifiedManifestsToDelete).toEqual([]); + }); + + test('correctly combines all cases', () => { + const newUnifiedManifests = existingUnifiedManifests.slice(0, 2).map((manifest) => ({ + ...manifest, + artifactIds: [ARTIFACT_ID_EXCEPTIONS_WINDOWS, ARTIFACT_ID_EXCEPTIONS_WINDOWS], + })); + + const newUnifiedManifestsAddition = { + policyId: 'test', + semanticVersion: '1.0.0', + artifactIds: [], + }; + + const expectedUnifiedManifestsToUpdate = bumpSemanticVersion(newUnifiedManifests); + + const expectedUnifiedManifestsToCreate = [newUnifiedManifestsAddition]; + + const expectedUnifiedManifestsToDelete = [existingUnifiedManifests[2].id]; + + const { unifiedManifestsToUpdate, unifiedManifestsToCreate, unifiedManifestsToDelete } = + manifestManager.prepareUnifiedManifestsSOUpdates( + [...newUnifiedManifests, newUnifiedManifestsAddition], + existingUnifiedManifests + ); + + expect(unifiedManifestsToUpdate).toEqual(expectedUnifiedManifestsToUpdate); + expect(unifiedManifestsToCreate).toEqual(expectedUnifiedManifestsToCreate); + expect(unifiedManifestsToDelete).toEqual(expectedUnifiedManifestsToDelete); + }); + }); + describe('bumpGlobalUnifiedManifestVersion', () => { + const createSoFindMock = (savedObjects: Array>) => + jest.fn().mockImplementation(async (objectType: { type: string }) => { + if (objectType.type === ManifestConstants.UNIFIED_SAVED_OBJECT_TYPE) { + return { + saved_objects: savedObjects, + }; + } else { + return null; + } + }); + + test('should bump the semantic version of the global manifest', async () => { + context.savedObjectsClient.find = createSoFindMock([ + { + id: '1', + attributes: { + policyId: '.global', + semanticVersion: '1.0.1', + }, + }, + ]); + context.savedObjectsClient.bulkUpdate = jest.fn(); + await manifestManager.bumpGlobalUnifiedManifestVersion(); + expect(context.savedObjectsClient.bulkUpdate).toHaveBeenCalledWith([ + { + id: '1', + type: ManifestConstants.UNIFIED_SAVED_OBJECT_TYPE, + attributes: { + policyId: '.global', + semanticVersion: '1.0.2', + }, + }, + ]); + }); + test('should make a clean return when no global manifest is found', async () => { + context.savedObjectsClient.find = createSoFindMock([]); + context.savedObjectsClient.bulkUpdate = jest.fn(); + await manifestManager.bumpGlobalUnifiedManifestVersion(); + expect(context.savedObjectsClient.bulkUpdate).toHaveBeenCalledTimes(0); + }); + }); + }); }); diff --git a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts index 663318c7459a3..3a6cfc5be280c 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts @@ -6,15 +6,17 @@ */ import semver from 'semver'; -import { isEmpty, isEqual, keyBy } from 'lodash'; +import { chunk, isEmpty, isEqual, keyBy } from 'lodash'; import type { ElasticsearchClient } from '@kbn/core/server'; import { type Logger, type SavedObjectsClientContract } from '@kbn/core/server'; -import { ENDPOINT_LIST_ID, ENDPOINT_ARTIFACT_LISTS } from '@kbn/securitysolution-list-constants'; +import { ENDPOINT_ARTIFACT_LISTS, ENDPOINT_LIST_ID } from '@kbn/securitysolution-list-constants'; import type { PackagePolicy } from '@kbn/fleet-plugin/common'; import type { Artifact, PackagePolicyClient } from '@kbn/fleet-plugin/server'; import type { ExceptionListClient } from '@kbn/lists-plugin/server'; import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import { ProductFeatureKey } from '@kbn/security-solution-features/keys'; +import { asyncForEach } from '@kbn/std'; +import { UnifiedManifestClient } from '../unified_manifest_client'; import { stringify } from '../../../utils/stringify'; import { QueueProcessor } from '../../../utils/queue_processor'; import type { ProductFeaturesService } from '../../../../lib/product_features_service/product_features_service'; @@ -35,9 +37,15 @@ import { Manifest, } from '../../../lib/artifacts'; +import type { + InternalUnifiedManifestBaseSchema, + InternalUnifiedManifestSchema, + InternalUnifiedManifestUpdateSchema, +} from '../../../schemas/artifacts'; import { internalArtifactCompleteSchema, type InternalArtifactCompleteSchema, + type InternalManifestSchema, type WrappedTranslatedExceptionList, } from '../../../schemas/artifacts'; import type { EndpointArtifactClientInterface } from '../artifact_client'; @@ -513,7 +521,25 @@ export class ManifestManager { */ public async getLastComputedManifest(): Promise { try { - const manifestSo = await this.getManifestClient().getManifest(); + let manifestSo; + if (this.experimentalFeatures.unifiedManifestEnabled) { + const unifiedManifestsSo = await this.getAllUnifiedManifestsSO(); + // On first run, there will be no existing Unified Manifests SO, so we need to copy the semanticVersion from the legacy manifest + // This is to ensure that the first Unified Manifest created has the same semanticVersion as the legacy manifest and is not too far + // behind for package policy to pick it up. + if (unifiedManifestsSo.length === 0) { + const legacyManifestSo = await this.getManifestClient().getManifest(); + const legacySemanticVersion = legacyManifestSo?.attributes?.semanticVersion; + manifestSo = this.transformUnifiedManifestSOtoLegacyManifestSO( + unifiedManifestsSo, + legacySemanticVersion + ); + } else { + manifestSo = this.transformUnifiedManifestSOtoLegacyManifestSO(unifiedManifestsSo); + } + } else { + manifestSo = await this.getManifestClient().getManifest(); + } if (manifestSo.version === undefined) { throw new InvalidInternalManifestError( @@ -721,21 +747,25 @@ export class ManifestManager { * @returns {Promise} An error, if encountered, or null. */ public async commit(manifest: Manifest) { - const manifestClient = this.getManifestClient(); - - // Commit the new manifest const manifestSo = manifest.toSavedObject(); - const version = manifest.getSavedObjectVersion(); - if (version == null) { - await manifestClient.createManifest(manifestSo); + if (this.experimentalFeatures.unifiedManifestEnabled) { + await this.commitUnified(manifestSo); } else { - await manifestClient.updateManifest(manifestSo, { - version, - }); - } + const manifestClient = this.getManifestClient(); - this.logger.debug(`Committed manifest ${manifest.getSemanticVersion()}`); + const version = manifest.getSavedObjectVersion(); + + if (version == null) { + await manifestClient.createManifest(manifestSo); + } else { + await manifestClient.updateManifest(manifestSo, { + version, + }); + } + + this.logger.debug(`Committed manifest ${manifest.getSemanticVersion()}`); + } } private fetchAllPolicies(): AsyncIterable { @@ -835,4 +865,229 @@ export class ManifestManager { ); } } + + /** + * Unified Manifest methods + */ + + private setNewSemanticVersion(semanticVersion: string): string | null { + const newSemanticVersion = semver.inc(semanticVersion, 'patch'); + if (!semver.valid(newSemanticVersion)) { + throw new Error(`Invalid semver: ${newSemanticVersion}`); + } + return newSemanticVersion; + } + + protected getUnifiedManifestClient(): UnifiedManifestClient { + return new UnifiedManifestClient(this.savedObjectsClient); + } + + public async getAllUnifiedManifestsSO(): Promise { + return this.getUnifiedManifestClient().getAllUnifiedManifests(); + } + + public transformUnifiedManifestSOtoLegacyManifestSO( + unifiedManifestsSo: InternalUnifiedManifestSchema[], + semanticVersion?: string + ): { + version: string; + attributes: { + artifacts: Array< + { artifactId: string; policyId: undefined } | { artifactId: string; policyId: string } + >; + semanticVersion: string; + schemaVersion: ManifestSchemaVersion; + }; + } { + const globalUnifiedManifest = unifiedManifestsSo.find((a) => a.policyId === '.global'); + return { + version: 'WzQ3NzAsMV0=', // version is hardcoded since it was used only to determine whether to create a new manifest or update an existing one + attributes: { + artifacts: [ + ...(globalUnifiedManifest?.artifactIds.map((artifactId) => ({ + artifactId, + policyId: undefined, + })) ?? []), + ...unifiedManifestsSo.reduce( + (acc: Array<{ artifactId: string; policyId: string }>, unifiedManifest) => { + if (unifiedManifest.policyId === '.global') { + return acc; + } + acc.push( + ...unifiedManifest.artifactIds.map((artifactId) => ({ + policyId: unifiedManifest.policyId, + artifactId, + })) + ); + + return acc; + }, + [] + ), + ], + semanticVersion: (semanticVersion || globalUnifiedManifest?.semanticVersion) ?? '1.0.0', + schemaVersion: this.schemaVersion, + }, + }; + } + + public transformLegacyManifestSOtoUnifiedManifestSO( + manifestSo: InternalManifestSchema, + unifiedManifestsSo: InternalUnifiedManifestSchema[] + ): Array { + const manifestObject = manifestSo.artifacts.reduce( + ( + acc: Record, + { artifactId, policyId = '.global' } + ) => { + const existingPolicy = acc[policyId]; + if (existingPolicy) { + existingPolicy.artifactIds.push(artifactId); + } else { + const existingUnifiedManifestSo = unifiedManifestsSo.find( + (item) => item.policyId === policyId + ); + + // On first run, there will be no existing Unified Manifests SO, so we need to copy the semanticVersion from the legacy manifest + // This is to ensure that the first Unified Manifest created has the same semanticVersion as the legacy manifest and is not too far + // behind for package policy to pick it up. + const semanticVersion = + (policyId === '.global' && !unifiedManifestsSo.length + ? manifestSo?.semanticVersion + : existingUnifiedManifestSo?.semanticVersion) ?? '1.0.0'; + + acc[policyId] = { + policyId, + artifactIds: [artifactId], + semanticVersion, + id: existingUnifiedManifestSo?.id, + }; + } + return acc; + }, + {} + ); + return Object.values(manifestObject); + } + + public prepareUnifiedManifestsSOUpdates( + unifiedManifestsSo: Array & { id?: string }>, + existingUnifiedManifestsSo: InternalUnifiedManifestSchema[] + ) { + const existingManifestsObj: Record = {}; + existingUnifiedManifestsSo.forEach((manifest) => { + existingManifestsObj[manifest.id] = manifest; + }); + + const { unifiedManifestsToUpdate, unifiedManifestsToCreate } = unifiedManifestsSo.reduce( + ( + acc: { + unifiedManifestsToUpdate: InternalUnifiedManifestUpdateSchema[]; + unifiedManifestsToCreate: InternalUnifiedManifestBaseSchema[]; + }, + unifiedManifest + ) => { + if (unifiedManifest.id !== undefined) { + // Manifest with id exists in SO, check if it needs to be updated + const existingUnifiedManifest = existingManifestsObj[unifiedManifest.id]; + // Update SO if the artifactIds changed. + if (!isEqual(existingUnifiedManifest.artifactIds, unifiedManifest.artifactIds)) { + acc.unifiedManifestsToUpdate.push({ + ...unifiedManifest, + semanticVersion: this.setNewSemanticVersion(unifiedManifest.semanticVersion), + version: existingUnifiedManifest.version, + } as InternalUnifiedManifestUpdateSchema); + } + } else { + // Manifest with id does not exist in SO, create new SO + acc.unifiedManifestsToCreate.push(unifiedManifest); + } + + return acc; + }, + { unifiedManifestsToUpdate: [], unifiedManifestsToCreate: [] } + ); + + const unifiedManifestsToDelete = existingUnifiedManifestsSo.reduce( + (acc: string[], { policyId, id }) => { + const existingPolicy = unifiedManifestsSo.find((item) => item.policyId === policyId); + if (!existingPolicy) { + acc.push(id); + } + return acc; + }, + [] + ); + + return { unifiedManifestsToUpdate, unifiedManifestsToCreate, unifiedManifestsToDelete }; + } + + public async bumpGlobalUnifiedManifestVersion(): Promise { + const globalUnifiedManifestSO = + await this.getUnifiedManifestClient().getUnifiedManifestByPolicyId('.global'); + if (!globalUnifiedManifestSO?.saved_objects?.length) { + this.logger.warn('No Global Unified Manifest found to bump version'); + return; + } + const globalUnifiedManifest = globalUnifiedManifestSO.saved_objects[0]; + + const newSemanticVersion = + this.setNewSemanticVersion(globalUnifiedManifest.attributes.semanticVersion) || '1.0.0'; + await this.getUnifiedManifestClient().updateUnifiedManifest({ + ...globalUnifiedManifest.attributes, + id: globalUnifiedManifest.id, + semanticVersion: newSemanticVersion, + }); + } + + public async commitUnified(manifestSo: InternalManifestSchema): Promise { + const existingUnifiedManifestsSo = await this.getAllUnifiedManifestsSO(); + + const unifiedManifestSO = this.transformLegacyManifestSOtoUnifiedManifestSO( + manifestSo, + existingUnifiedManifestsSo + ); + + const { unifiedManifestsToUpdate, unifiedManifestsToCreate, unifiedManifestsToDelete } = + this.prepareUnifiedManifestsSOUpdates(unifiedManifestSO, existingUnifiedManifestsSo); + + if (unifiedManifestsToCreate.length) { + await asyncForEach(chunk(unifiedManifestsToCreate, 100), async (unifiedManifestsBatch) => { + await this.getUnifiedManifestClient().createUnifiedManifests(unifiedManifestsBatch); + }); + this.logger.debug(`Created ${unifiedManifestsToCreate.length} unified manifests`); + } + + if (unifiedManifestsToUpdate.length) { + await asyncForEach(chunk(unifiedManifestsToUpdate, 100), async (unifiedManifestsBatch) => { + await this.getUnifiedManifestClient().updateUnifiedManifests(unifiedManifestsBatch); + }); + + this.logger.debug(`Updated ${unifiedManifestsToUpdate.length} unified manifests`); + } + + if (unifiedManifestsToDelete.length) { + await asyncForEach(chunk(unifiedManifestsToDelete, 100), async (unifiedManifestsBatch) => { + await this.getUnifiedManifestClient().deleteUnifiedManifestByIds(unifiedManifestsBatch); + }); + + this.logger.debug(`Deleted ${unifiedManifestsToDelete.length} unified manifests`); + } + + if ( + unifiedManifestsToCreate.length || + unifiedManifestsToUpdate.length || + unifiedManifestsToDelete.length + ) { + // If global manifest is not in the list of manifests to create or update, we need to bump its version + // We use it to set schemaVersion of the legacy manifest we are going to create so that its being picked up when populating agent policy + const hasGlobalManifest = [...unifiedManifestsToCreate, ...unifiedManifestsToUpdate].some( + (manifest) => manifest.policyId === '.global' + ); + + if (!hasGlobalManifest || unifiedManifestsToDelete.length) { + await this.bumpGlobalUnifiedManifestVersion(); + } + } + } } diff --git a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/unified_manifes_client.test.ts b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/unified_manifes_client.test.ts index 83a37e617caf7..4fde69946ccff 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/unified_manifes_client.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/unified_manifes_client.test.ts @@ -73,8 +73,7 @@ describe('unified_manifest_client', () => { test('can get unified manifest by id', async () => { await unifiedManifestClient.getUnifiedManifestById('123'); expect(savedObjectsClient.bulkGet).toHaveBeenCalledWith( - expect.arrayContaining([mockSoClientCallParams({ id: '123' }, false)]), - { namespace: UNIFIED_MANIFEST_ALL_NAMESPACES } + expect.arrayContaining([mockSoClientCallParams({ id: '123' }, false)]) ); }); @@ -84,8 +83,7 @@ describe('unified_manifest_client', () => { expect.arrayContaining([ mockSoClientCallParams({ id: '123' }, false), mockSoClientCallParams({ id: '456' }, false), - ]), - { namespace: UNIFIED_MANIFEST_ALL_NAMESPACES } + ]) ); }); @@ -114,17 +112,14 @@ describe('unified_manifest_client', () => { ...mockUnifiedManifestAttributes({ policyId: `policy-${i}` }), id: `id-${i}`, created: '1', + version: '1', }; }) ); - const cbFunc = jest.fn(); - await unifiedManifestClient.getAllUnifiedManifests(cbFunc); + const result = await unifiedManifestClient.getAllUnifiedManifests(); - expect(cbFunc).toHaveBeenCalledTimes(3); - expect(cbFunc).toHaveBeenLastCalledWith([ - expect.objectContaining({ policyId: 'policy-2000', id: 'id-2000' }), - ]); + expect(result.length).toBe(2001); }); }); @@ -140,8 +135,7 @@ describe('unified_manifest_client', () => { expect(savedObjectsClient.bulkUpdate).toHaveBeenCalledWith( expect.arrayContaining([ mockSoClientCallParams({ id: '1234', version: 'abcd' }, true, false), - ]), - { namespace: UNIFIED_MANIFEST_ALL_NAMESPACES } + ]) ); }); test('can update unified manifests', async () => { @@ -153,8 +147,7 @@ describe('unified_manifest_client', () => { expect.arrayContaining([ mockSoClientCallParams({ id: '1234', version: 'abcd' }, true, false), mockSoClientCallParams({ id: '1234', version: 'abcd' }, true, false), - ]), - { namespace: UNIFIED_MANIFEST_ALL_NAMESPACES } + ]) ); }); }); @@ -162,8 +155,7 @@ describe('unified_manifest_client', () => { test('can delete unified manifest', async () => { await unifiedManifestClient.deleteUnifiedManifestById('123'); expect(savedObjectsClient.bulkDelete).toHaveBeenCalledWith( - expect.arrayContaining([{ id: '123', type: ManifestConstants.UNIFIED_SAVED_OBJECT_TYPE }]), - { namespace: UNIFIED_MANIFEST_ALL_NAMESPACES } + expect.arrayContaining([{ id: '123', type: ManifestConstants.UNIFIED_SAVED_OBJECT_TYPE }]) ); }); test('can delete unified manifests', async () => { @@ -172,8 +164,7 @@ describe('unified_manifest_client', () => { expect.arrayContaining([ mockSoClientCallParams({ id: '123' }, false), mockSoClientCallParams({ id: '456' }, false), - ]), - { namespace: UNIFIED_MANIFEST_ALL_NAMESPACES } + ]) ); }); }); diff --git a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/unified_manifest_client.ts b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/unified_manifest_client.ts index db47c43b68607..d84742ed65f10 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/unified_manifest_client.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/unified_manifest_client.ts @@ -86,20 +86,19 @@ export class UnifiedManifestClient { manifestIds: string[] ): Promise> { return this.savedObjectsClient.bulkGet( - manifestIds.map((id) => ({ id, type: ManifestConstants.UNIFIED_SAVED_OBJECT_TYPE })), - { namespace: UNIFIED_MANIFEST_ALL_NAMESPACES } + manifestIds.map((id) => ({ id, type: ManifestConstants.UNIFIED_SAVED_OBJECT_TYPE })) ); } public async getAllUnifiedManifests( - cb: (unifiedManifests: InternalUnifiedManifestSchema[]) => void | Promise, options?: FetchAllUnifiedManifestsOptions - ): Promise { + ): Promise { const unifiedManifestsFetcher = this.fetchAllUnifiedManifests(this.savedObjectsClient, options); - + const allUnifiedManifests: InternalUnifiedManifestSchema[] = []; for await (const unifiedManifests of unifiedManifestsFetcher) { - await cb(unifiedManifests); + allUnifiedManifests.push(...unifiedManifests); } + return allUnifiedManifests; } /** @@ -127,8 +126,7 @@ export class UnifiedManifestClient { attributes, ...(version ? { version } : {}), }; - }), - { namespace: UNIFIED_MANIFEST_ALL_NAMESPACES } + }) ); } @@ -144,8 +142,7 @@ export class UnifiedManifestClient { manifestIds: string[] ): Promise { return this.savedObjectsClient.bulkDelete( - manifestIds.map((id) => ({ id, type: ManifestConstants.UNIFIED_SAVED_OBJECT_TYPE })), - { namespace: UNIFIED_MANIFEST_ALL_NAMESPACES } + manifestIds.map((id) => ({ id, type: ManifestConstants.UNIFIED_SAVED_OBJECT_TYPE })) ); } @@ -160,7 +157,7 @@ export class UnifiedManifestClient { fields = [], kuery, sortOrder = 'asc', - sortField = 'created', + sortField = 'created_at', }: FetchAllUnifiedManifestsOptions = {} ): AsyncIterable { return createSoFindIterable({ diff --git a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/utils.ts b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/utils.ts index f52eafbdc4529..de1484bf155a1 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/utils.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/utils.ts @@ -16,6 +16,7 @@ export const mapUnifiedManifestSavedObjectToUnifiedManifest = ({ attributes: { artifactIds, policyId, semanticVersion }, // eslint-disable-next-line @typescript-eslint/naming-convention created_at, + version, }: SavedObject): InternalUnifiedManifestSchema => { return { id, @@ -23,5 +24,6 @@ export const mapUnifiedManifestSavedObjectToUnifiedManifest = ({ semanticVersion, created: created_at, artifactIds, + version, }; }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/convert_rule_to_diffable.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/convert_rule_to_diffable.ts index 95a8687324d1c..c08edbc0c5cc5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/convert_rule_to_diffable.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/convert_rule_to_diffable.ts @@ -53,6 +53,7 @@ import { extractRuleNameOverrideObject } from './extract_rule_name_override_obje import { extractRuleSchedule } from './extract_rule_schedule'; import { extractTimelineTemplateReference } from './extract_timeline_template_reference'; import { extractTimestampOverrideObject } from './extract_timestamp_override_object'; +import { addEcsToRequiredFields } from '../../../../rule_management/utils/utils'; /** * Normalizes a given rule to the form which is suitable for passing to the diff algorithm. @@ -133,7 +134,7 @@ const extractDiffableCommonFields = ( note: rule.note ?? '', setup: rule.setup ?? '', related_integrations: rule.related_integrations ?? [], - required_fields: rule.required_fields ?? [], + required_fields: addEcsToRequiredFields(rule.required_fields), author: rule.author ?? [], license: rule.license ?? '', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.ts index 40a3d048fb518..38ead608a3b75 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.ts @@ -7,7 +7,6 @@ import * as z from 'zod'; import { - RequiredFieldArray, RuleSignatureId, RuleVersion, BaseCreateProps, @@ -33,6 +32,5 @@ export const PrebuiltRuleAsset = BaseCreateProps.and(TypeSpecificCreateProps).an z.object({ rule_id: RuleSignatureId, version: RuleVersion, - required_fields: RequiredFieldArray.optional(), }) ); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts index d36f0ab4ad66e..ec790f9f6f71b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts @@ -14,6 +14,7 @@ import { transformRuleToAlertAction } from '../../../../../../common/detection_e import type { InternalRuleUpdate, RuleParams, RuleAlertType } from '../../../rule_schema'; import { transformToActionFrequency } from '../../normalization/rule_actions'; import { typeSpecificSnakeToCamel } from '../../normalization/rule_converters'; +import { addEcsToRequiredFields } from '../../utils/utils'; export interface UpdateRulesOptions { rulesClient: RulesClient; @@ -38,6 +39,7 @@ export const updateRules = async ({ const typeSpecificParams = typeSpecificSnakeToCamel(ruleUpdate); const enabled = ruleUpdate.enabled ?? true; + const newInternalRule: InternalRuleUpdate = { name: ruleUpdate.name, tags: ruleUpdate.tags ?? [], @@ -58,7 +60,7 @@ export const updateRules = async ({ meta: ruleUpdate.meta, maxSignals: ruleUpdate.max_signals ?? DEFAULT_MAX_SIGNALS, relatedIntegrations: ruleUpdate.related_integrations ?? [], - requiredFields: existingRule.params.requiredFields, + requiredFields: addEcsToRequiredFields(ruleUpdate.required_fields), riskScore: ruleUpdate.risk_score, riskScoreMapping: ruleUpdate.risk_score_mapping ?? [], ruleNameOverride: ruleUpdate.rule_name_override, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.test.ts index d0a14151cabac..537d7b6abaf8a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.test.ts @@ -13,6 +13,7 @@ import { import { getBaseRuleParams, getEqlRuleParams, + getEsqlRuleParams, getMlRuleParams, getNewTermsRuleParams, getQueryRuleParams, @@ -219,6 +220,27 @@ describe('rule_converters', () => { ); }); + test('should accept ES|QL alerts suppression params', () => { + const patchParams = { + alert_suppression: { + group_by: ['agent.name'], + duration: { value: 4, unit: 'h' as const }, + missing_fields_strategy: 'doNotSuppress' as const, + }, + }; + const rule = getEsqlRuleParams(); + const patchedParams = patchTypeSpecificSnakeToCamel(patchParams, rule); + expect(patchedParams).toEqual( + expect.objectContaining({ + alertSuppression: { + groupBy: ['agent.name'], + missingFieldsStrategy: 'doNotSuppress', + duration: { value: 4, unit: 'h' }, + }, + }) + ); + }); + test('should accept threshold alerts suppression params', () => { const patchParams = { alert_suppression: { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.ts index 50a1cecce88c8..fd77213b178b5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.ts @@ -22,7 +22,6 @@ import { import type { PatchRuleRequestBody } from '../../../../../common/api/detection_engine/rule_management'; import type { RelatedIntegrationArray, - RequiredFieldArray, RuleCreateProps, TypeSpecificCreateProps, TypeSpecificResponse, @@ -78,6 +77,7 @@ import type { } from '../../rule_schema'; import { transformFromAlertThrottle, transformToActionFrequency } from './rule_actions'; import { + addEcsToRequiredFields, convertAlertSuppressionToCamel, convertAlertSuppressionToSnake, migrateLegacyInvestigationFields, @@ -121,6 +121,7 @@ export const typeSpecificSnakeToCamel = ( type: params.type, language: params.language, query: params.query, + alertSuppression: convertAlertSuppressionToCamel(params.alert_suppression), }; } case 'threat_match': { @@ -237,6 +238,8 @@ const patchEsqlParams = ( type: existingRule.type, language: params.language ?? existingRule.language, query: params.query ?? existingRule.query, + alertSuppression: + convertAlertSuppressionToCamel(params.alert_suppression) ?? existingRule.alertSuppression, }; }; @@ -431,7 +434,6 @@ export const patchTypeSpecificSnakeToCamel = ( export const convertPatchAPIToInternalSchema = ( nextParams: PatchRuleRequestBody & { related_integrations?: RelatedIntegrationArray; - required_fields?: RequiredFieldArray; }, existingRule: SanitizedRule ): InternalRuleUpdate => { @@ -462,7 +464,7 @@ export const convertPatchAPIToInternalSchema = ( meta: nextParams.meta ?? existingParams.meta, maxSignals: nextParams.max_signals ?? existingParams.maxSignals, relatedIntegrations: nextParams.related_integrations ?? existingParams.relatedIntegrations, - requiredFields: nextParams.required_fields ?? existingParams.requiredFields, + requiredFields: addEcsToRequiredFields(nextParams.required_fields), riskScore: nextParams.risk_score ?? existingParams.riskScore, riskScoreMapping: nextParams.risk_score_mapping ?? existingParams.riskScoreMapping, ruleNameOverride: nextParams.rule_name_override ?? existingParams.ruleNameOverride, @@ -487,11 +489,9 @@ export const convertPatchAPIToInternalSchema = ( }; }; -// eslint-disable-next-line complexity export const convertCreateAPIToInternalSchema = ( input: RuleCreateProps & { related_integrations?: RelatedIntegrationArray; - required_fields?: RequiredFieldArray; }, immutable = false, defaultEnabled = true @@ -537,7 +537,7 @@ export const convertCreateAPIToInternalSchema = ( version: input.version ?? 1, exceptionsList: input.exceptions_list ?? [], relatedIntegrations: input.related_integrations ?? [], - requiredFields: input.required_fields ?? [], + requiredFields: addEcsToRequiredFields(input.required_fields), setup: input.setup ?? '', ...typeSpecificParams, }, @@ -571,6 +571,7 @@ export const typeSpecificCamelToSnake = ( type: params.type, language: params.language, query: params.query, + alert_suppression: convertAlertSuppressionToSnake(params.alertSuppression), }; } case 'threat_match': { @@ -776,6 +777,7 @@ export const convertPrebuiltRuleAssetToRuleResponse = ( return RuleResponse.parse({ ...prebuiltRuleAssetDefaults, ...prebuiltRuleAsset, + required_fields: addEcsToRequiredFields(prebuiltRuleAsset.required_fields), ...ruleResponseSpecificFields, }); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/utils.ts index bda8cd7a688ca..66fa635e768ad 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/utils.ts @@ -9,6 +9,8 @@ import { partition } from 'lodash/fp'; import pMap from 'p-map'; import { v4 as uuidv4 } from 'uuid'; +import { ecsFieldMap } from '@kbn/alerts-as-data-utils'; + import type { ActionsClient, FindActionResult } from '@kbn/actions-plugin/server'; import type { FindResult, PartialRule } from '@kbn/alerting-plugin/server'; import type { SavedObjectsClientContract } from '@kbn/core/server'; @@ -18,6 +20,8 @@ import type { AlertSuppression, AlertSuppressionCamel, InvestigationFields, + RequiredField, + RequiredFieldInput, RuleResponse, } from '../../../../../common/api/detection_engine/model/rule_schema'; import type { @@ -388,3 +392,19 @@ export const migrateLegacyInvestigationFields = ( return investigationFields; }; + +/* + Computes the boolean "ecs" property value for each required field based on the ECS field map. + "ecs" property indicates whether the required field is an ECS field or not. +*/ +export const addEcsToRequiredFields = (requiredFields?: RequiredFieldInput[]): RequiredField[] => + (requiredFields ?? []).map((requiredFieldWithoutEcs) => { + const isEcsField = Boolean( + ecsFieldMap[requiredFieldWithoutEcs.name]?.type === requiredFieldWithoutEcs.type + ); + + return { + ...requiredFieldWithoutEcs, + ecs: isEcsField, + }; + }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts index 9ebf17caf9a85..3a4fa1dadd778 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts @@ -12,6 +12,7 @@ import type { BaseRuleParams, CompleteRule, EqlRuleParams, + EsqlRuleParams, MachineLearningRuleParams, NewTermsRuleParams, QueryRuleParams, @@ -103,6 +104,16 @@ export const getEqlRuleParams = (rewrites?: Partial): EqlRulePara }; }; +export const getEsqlRuleParams = (rewrites?: Partial): EsqlRuleParams => { + return { + ...getBaseRuleParams(), + type: 'esql', + language: 'esql', + query: 'from auditbeat* metadata _id', + ...rewrites, + }; +}; + export const getMlRuleParams = ( rewrites?: Partial ): MachineLearningRuleParams => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts index d116f6fd71209..120ddd3981165 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts @@ -169,6 +169,7 @@ export const EsqlSpecificRuleParams = z.object({ type: z.literal('esql'), language: z.literal('esql'), query: RuleQuery, + alertSuppression: AlertSuppressionCamel.optional(), }); export type EsqlRuleParams = BaseRuleParams & EsqlSpecificRuleParams; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/create_esql_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/create_esql_alert_type.ts index 15ff7adb11013..10c82ad8fed7c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/create_esql_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/create_esql_alert_type.ts @@ -16,7 +16,7 @@ import type { CreateRuleOptions, SecurityAlertType } from '../types'; export const createEsqlAlertType = ( createOptions: CreateRuleOptions ): SecurityAlertType => { - const { version } = createOptions; + const { version, experimentalFeatures, licensing } = createOptions; return { id: ESQL_RULE_TYPE_ID, name: 'ES|QL Rule', @@ -44,6 +44,6 @@ export const createEsqlAlertType = ( isExportable: false, category: DEFAULT_APP_CATEGORIES.security.id, producer: SERVER_APP_ID, - executor: (params) => esqlExecutor({ ...params, version }), + executor: (params) => esqlExecutor({ ...params, experimentalFeatures, version, licensing }), }; }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/esql.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/esql.ts index 64ed0560f3609..3887f5db81a5a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/esql.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/esql.ts @@ -11,19 +11,24 @@ import type { AlertInstanceState, RuleExecutorServices, } from '@kbn/alerting-plugin/server'; +import type * as estypes from '@elastic/elasticsearch/lib/api/types'; import { computeIsESQLQueryAggregating, getIndexListFromEsqlQuery, } from '@kbn/securitysolution-utils'; +import type { LicensingPluginSetup } from '@kbn/licensing-plugin/server'; import { buildEsqlSearchRequest } from './build_esql_search_request'; import { performEsqlRequest } from './esql_request'; import { wrapEsqlAlerts } from './wrap_esql_alerts'; +import { wrapSuppressedEsqlAlerts } from './wrap_suppressed_esql_alerts'; +import { bulkCreateSuppressedAlertsInMemory } from '../utils/bulk_create_suppressed_alerts_in_memory'; import { createEnrichEventsFunction } from '../utils/enrichments'; import { rowToDocument } from './utils'; import { fetchSourceDocuments } from './fetch_source_documents'; +import { buildReasonMessageForEsqlAlert } from '../utils/reason_formatters'; -import type { RunOpts } from '../types'; +import type { RunOpts, SignalSource } from '../types'; import { addToSearchAfterReturn, @@ -31,16 +36,12 @@ import { makeFloatString, getUnprocessedExceptionsWarnings, getMaxSignalsWarning, + getSuppressionMaxSignalsWarning, } from '../utils/utils'; import type { EsqlRuleParams } from '../../rule_schema'; import { withSecuritySpan } from '../../../../utils/with_security_span'; - -/** - * ES|QL returns results as a single page. max size of 10,000 - * while we try increase size of the request to catch all events - * we don't want to overload ES/Kibana with large responses - */ -const ESQL_PAGE_SIZE_CIRCUIT_BREAKER = 1000; +import { getIsAlertSuppressionActive } from '../utils/get_is_alert_suppression_active'; +import type { ExperimentalFeatures } from '../../../../../common'; export const esqlExecutor = async ({ runOpts: { @@ -55,18 +56,29 @@ export const esqlExecutor = async ({ unprocessedExceptions, alertTimestampOverride, publicBaseUrl, + alertWithSuppression, }, services, state, spaceId, + experimentalFeatures, + licensing, }: { runOpts: RunOpts; services: RuleExecutorServices; state: object; spaceId: string; version: string; + experimentalFeatures: ExperimentalFeatures; + licensing: LicensingPluginSetup; }) => { const ruleParams = completeRule.ruleParams; + /** + * ES|QL returns results as a single page. max size of 10,000 + * while we try increase size of the request to catch all alerts that might been deduplicated + * we don't want to overload ES/Kibana with large responses + */ + const ESQL_PAGE_SIZE_CIRCUIT_BREAKER = tuple.maxSignals * 3; return withSecuritySpan('esqlExecutor', async () => { const result = createSearchAfterReturnType(); @@ -120,35 +132,100 @@ export const esqlExecutor = async ({ isRuleAggregating, }); - const wrappedAlerts = wrapEsqlAlerts({ - sourceDocuments, - isRuleAggregating, - results, - spaceId, - completeRule, - mergeStrategy, - alertTimestampOverride, - ruleExecutionLogger, - publicBaseUrl, - tuple, + const isAlertSuppressionActive = await getIsAlertSuppressionActive({ + alertSuppression: completeRule.ruleParams.alertSuppression, + licensing, + isFeatureDisabled: !experimentalFeatures?.alertSuppressionForEsqlRuleEnabled, }); - const enrichAlerts = createEnrichEventsFunction({ - services, - logger: ruleExecutionLogger, + const wrapHits = (events: Array>) => + wrapEsqlAlerts({ + events, + spaceId, + completeRule, + mergeStrategy, + isRuleAggregating, + alertTimestampOverride, + ruleExecutionLogger, + publicBaseUrl, + tuple, + }); + + const syntheticHits: Array> = results.map((document) => { + const { _id, _version, _index, ...source } = document; + + return { + _source: source as SignalSource, + fields: _id ? sourceDocuments[_id]?.fields : {}, + _id: _id ?? '', + _index: _index ?? '', + }; }); - const bulkCreateResult = await bulkCreate( - wrappedAlerts, - tuple.maxSignals - result.createdSignalsCount, - enrichAlerts - ); - addToSearchAfterReturn({ current: result, next: bulkCreateResult }); - ruleExecutionLogger.debug(`Created ${bulkCreateResult.createdItemsCount} alerts`); + if (isAlertSuppressionActive) { + const wrapSuppressedHits = (events: Array>) => + wrapSuppressedEsqlAlerts({ + events, + spaceId, + completeRule, + mergeStrategy, + isRuleAggregating, + alertTimestampOverride, + ruleExecutionLogger, + publicBaseUrl, + primaryTimestamp, + secondaryTimestamp, + tuple, + }); + + const bulkCreateResult = await bulkCreateSuppressedAlertsInMemory({ + enrichedEvents: syntheticHits, + toReturn: result, + wrapHits, + bulkCreate, + services, + ruleExecutionLogger, + tuple, + alertSuppression: completeRule.ruleParams.alertSuppression, + wrapSuppressedHits, + alertTimestampOverride, + alertWithSuppression, + experimentalFeatures, + buildReasonMessage: buildReasonMessageForEsqlAlert, + mergeSourceAndFields: true, + // passing 1 here since ES|QL does not support pagination + maxNumberOfAlertsMultiplier: 1, + }); + + addToSearchAfterReturn({ current: result, next: bulkCreateResult }); + ruleExecutionLogger.debug( + `Created ${bulkCreateResult.createdItemsCount} alerts. Suppressed ${bulkCreateResult.suppressedItemsCount} alerts` + ); + + if (bulkCreateResult.alertsWereTruncated) { + result.warningMessages.push(getSuppressionMaxSignalsWarning()); + break; + } + } else { + const wrappedAlerts = wrapHits(syntheticHits); + + const enrichAlerts = createEnrichEventsFunction({ + services, + logger: ruleExecutionLogger, + }); + const bulkCreateResult = await bulkCreate( + wrappedAlerts, + tuple.maxSignals - result.createdSignalsCount, + enrichAlerts + ); - if (bulkCreateResult.alertsWereTruncated) { - result.warningMessages.push(getMaxSignalsWarning()); - break; + addToSearchAfterReturn({ current: result, next: bulkCreateResult }); + ruleExecutionLogger.debug(`Created ${bulkCreateResult.createdItemsCount} alerts`); + + if (bulkCreateResult.alertsWereTruncated) { + result.warningMessages.push(getMaxSignalsWarning()); + break; + } } // no more results will be found diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/utils/generate_alert_id.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/utils/generate_alert_id.test.ts new file mode 100644 index 0000000000000..b3bc78e6b9478 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/utils/generate_alert_id.test.ts @@ -0,0 +1,156 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { generateAlertId } from './generate_alert_id'; +import type * as estypes from '@elastic/elasticsearch/lib/api/types'; +import type { SignalSource } from '../../types'; +import type { CompleteRule, EsqlRuleParams } from '../../../rule_schema'; +import moment from 'moment'; +import { cloneDeep } from 'lodash'; + +const mockEvent: estypes.SearchHit = { + _id: 'test_id', + _version: 2, + _index: 'test_index', +}; + +const mockRule = { + alertId: 'test_alert_id', + ruleParams: { + query: 'from auditbeat*', + }, +} as CompleteRule; + +describe('generateAlertId', () => { + describe('aggregating query', () => { + const aggIdParams = { + event: mockEvent, + spaceId: 'default', + completeRule: mockRule, + tuple: { + to: moment('2010-10-20 04:43:12'), + from: moment('2010-10-20 04:10:12'), + maxSignals: 100, + }, + isRuleAggregating: true, + index: 10, + }; + + const id = generateAlertId(aggIdParams); + let modifiedIdParams: Parameters['0']; + + beforeEach(() => { + modifiedIdParams = cloneDeep(aggIdParams); + }); + + it('creates id dependant on time range tuple', () => { + modifiedIdParams.tuple.from = moment('2010-10-20 04:20:12'); + expect(id).not.toBe(generateAlertId(modifiedIdParams)); + }); + + it('creates id dependant on data row index', () => { + modifiedIdParams.index = 11; + expect(id).not.toBe(generateAlertId(modifiedIdParams)); + }); + + it('creates id dependant on spaceId', () => { + modifiedIdParams.spaceId = 'test-1'; + expect(id).not.toBe(generateAlertId(modifiedIdParams)); + }); + + it('creates id not dependant on event._id', () => { + modifiedIdParams.event._id = 'another-id'; + expect(id).toBe(generateAlertId(modifiedIdParams)); + }); + it('creates id not dependant on event._version', () => { + modifiedIdParams.event._version = 100; + expect(id).toBe(generateAlertId(modifiedIdParams)); + }); + it('creates id not dependant on event._index', () => { + modifiedIdParams.event._index = 'packetbeat-*'; + expect(id).toBe(generateAlertId(modifiedIdParams)); + }); + it('creates id dependant on rule alertId', () => { + modifiedIdParams.completeRule.alertId = 'another-alert-id'; + expect(id).not.toBe(generateAlertId(modifiedIdParams)); + }); + + it('creates id dependant on rule query', () => { + modifiedIdParams.completeRule.ruleParams.query = 'from packetbeat*'; + expect(id).not.toBe(generateAlertId(modifiedIdParams)); + }); + }); + + describe('non-aggregating query', () => { + const nonAggIdParams = { + event: mockEvent, + spaceId: 'default', + completeRule: mockRule, + tuple: { + to: moment('2010-10-20 04:43:12'), + from: moment('2010-10-20 04:10:12'), + maxSignals: 100, + }, + isRuleAggregating: false, + index: 10, + }; + + const id = generateAlertId(nonAggIdParams); + let modifiedIdParams: Parameters['0']; + + beforeEach(() => { + modifiedIdParams = cloneDeep(nonAggIdParams); + }); + + it('creates id not dependant on time range tuple', () => { + modifiedIdParams.tuple.from = moment('2010-10-20 04:20:12'); + expect(id).toBe(generateAlertId(modifiedIdParams)); + }); + + it('creates id not dependant on data row index', () => { + modifiedIdParams.index = 11; + expect(id).toBe(generateAlertId(modifiedIdParams)); + }); + + it('creates id dependant on spaceId', () => { + modifiedIdParams.spaceId = 'test-1'; + expect(id).not.toBe(generateAlertId(modifiedIdParams)); + }); + + it('creates id dependant on event._id', () => { + modifiedIdParams.event._id = 'another-id'; + expect(id).not.toBe(generateAlertId(modifiedIdParams)); + }); + it('creates id dependant on event._version', () => { + modifiedIdParams.event._version = 100; + expect(id).not.toBe(generateAlertId(modifiedIdParams)); + }); + it('creates id dependant on event._index', () => { + modifiedIdParams.event._index = 'packetbeat-*'; + expect(id).not.toBe(generateAlertId(modifiedIdParams)); + }); + it('creates id dependant on rule alertId', () => { + modifiedIdParams.completeRule.alertId = 'another-alert-id'; + expect(id).not.toBe(generateAlertId(modifiedIdParams)); + }); + + it('creates id not dependant on rule query', () => { + modifiedIdParams.completeRule.ruleParams.query = 'from packetbeat*'; + expect(id).toBe(generateAlertId(modifiedIdParams)); + }); + + it('creates id dependant on suppression terms', () => { + modifiedIdParams.suppressionTerms = [{ field: 'agent.name', value: ['test-1'] }]; + const id1 = generateAlertId(modifiedIdParams); + modifiedIdParams.suppressionTerms = [{ field: 'agent.name', value: ['test-2'] }]; + const id2 = generateAlertId(modifiedIdParams); + + expect(id).not.toBe(id1); + expect(id1).not.toBe(id2); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/utils/generate_alert_id.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/utils/generate_alert_id.ts new file mode 100644 index 0000000000000..0f549783f922e --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/utils/generate_alert_id.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import objectHash from 'object-hash'; +import type { Moment } from 'moment'; +import type * as estypes from '@elastic/elasticsearch/lib/api/types'; + +import type { CompleteRule, EsqlRuleParams } from '../../../rule_schema'; +import type { SignalSource } from '../../types'; +import type { SuppressionTerm } from '../../utils/suppression_utils'; +/** + * Generates id for ES|QL alert. + * Id is generated as hash of event properties and rule/space config identifiers. + * This would allow to deduplicate alerts, generated from the same event. + */ +export const generateAlertId = ({ + event, + spaceId, + completeRule, + tuple, + isRuleAggregating, + index, + suppressionTerms, +}: { + isRuleAggregating: boolean; + event: estypes.SearchHit; + spaceId: string | null | undefined; + completeRule: CompleteRule; + tuple: { + to: Moment; + from: Moment; + maxSignals: number; + }; + index: number; + suppressionTerms?: SuppressionTerm[]; +}) => { + const ruleRunId = tuple.from.toISOString() + tuple.to.toISOString(); + + return !isRuleAggregating && event._id + ? objectHash([ + event._id, + event._version, + event._index, + `${spaceId}:${completeRule.alertId}`, + ...(suppressionTerms ? [suppressionTerms] : []), + ]) + : objectHash([ + ruleRunId, + completeRule.ruleParams.query, + `${spaceId}:${completeRule.alertId}`, + index, + ]); +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/utils/index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/utils/index.ts index 061ddfda93174..4b2b842680e32 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/utils/index.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/utils/index.ts @@ -6,3 +6,4 @@ */ export * from './row_to_document'; +export * from './generate_alert_id'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/wrap_esql_alerts.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/wrap_esql_alerts.test.ts new file mode 100644 index 0000000000000..d54f91c088958 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/wrap_esql_alerts.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { cloneDeep } from 'lodash'; +import moment from 'moment'; + +import { ALERT_UUID } from '@kbn/rule-data-utils'; +import { getCompleteRuleMock, getEsqlRuleParams } from '../../rule_schema/mocks'; +import { ruleExecutionLogMock } from '../../rule_monitoring/mocks'; +import { sampleDocNoSortIdWithTimestamp } from '../__mocks__/es_results'; +import { wrapEsqlAlerts } from './wrap_esql_alerts'; + +import * as esqlUtils from './utils/generate_alert_id'; + +const ruleExecutionLogger = ruleExecutionLogMock.forExecutors.create(); + +const docId = 'd5e8eb51-a6a0-456d-8a15-4b79bfec3d71'; +const publicBaseUrl = 'http://somekibanabaseurl.com'; + +const alertSuppression = { + groupBy: ['source.ip'], +}; + +const completeRule = getCompleteRuleMock(getEsqlRuleParams()); +completeRule.ruleParams.alertSuppression = alertSuppression; + +describe('wrapSuppressedEsqlAlerts', () => { + test('should create an alert with the correct _id from a document', () => { + const doc = sampleDocNoSortIdWithTimestamp(docId); + const alerts = wrapEsqlAlerts({ + events: [doc], + isRuleAggregating: false, + spaceId: 'default', + mergeStrategy: 'missingFields', + completeRule, + alertTimestampOverride: undefined, + ruleExecutionLogger, + publicBaseUrl, + tuple: { + to: moment('2010-10-20 04:43:12'), + from: moment('2010-10-20 04:43:12'), + maxSignals: 100, + }, + }); + + expect(alerts[0]._id).toEqual('ed7fbf575371c898e0f0aea48cdf0bf1865939a9'); + expect(alerts[0]._source[ALERT_UUID]).toEqual('ed7fbf575371c898e0f0aea48cdf0bf1865939a9'); + }); + + test('should call generateAlertId for alert id', () => { + jest.spyOn(esqlUtils, 'generateAlertId').mockReturnValueOnce('mocked-alert-id'); + const completeRuleCloned = cloneDeep(completeRule); + completeRuleCloned.ruleParams.alertSuppression = { + groupBy: ['someKey'], + }; + const doc = sampleDocNoSortIdWithTimestamp(docId); + const alerts = wrapEsqlAlerts({ + events: [doc], + spaceId: 'default', + isRuleAggregating: true, + mergeStrategy: 'missingFields', + completeRule: completeRuleCloned, + alertTimestampOverride: undefined, + ruleExecutionLogger, + publicBaseUrl, + tuple: { + to: moment('2010-10-20 04:43:12'), + from: moment('2010-10-20 04:43:12'), + maxSignals: 100, + }, + }); + + expect(alerts[0]._id).toEqual('mocked-alert-id'); + expect(alerts[0]._source[ALERT_UUID]).toEqual('mocked-alert-id'); + + expect(esqlUtils.generateAlertId).toHaveBeenCalledWith( + expect.objectContaining({ + completeRule: expect.any(Object), + event: expect.any(Object), + index: 0, + isRuleAggregating: true, + spaceId: 'default', + tuple: { + from: expect.any(Object), + maxSignals: 100, + to: expect.any(Object), + }, + }) + ); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/wrap_esql_alerts.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/wrap_esql_alerts.ts index 6d3493e974668..b0fa2fd6638fa 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/wrap_esql_alerts.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/wrap_esql_alerts.ts @@ -5,7 +5,6 @@ * 2.0. */ -import objectHash from 'object-hash'; import type { Moment } from 'moment'; import type * as estypes from '@elastic/elasticsearch/lib/api/types'; @@ -19,9 +18,10 @@ import { buildReasonMessageForNewTermsAlert } from '../utils/reason_formatters'; import type { IRuleExecutionLogForExecutors } from '../../rule_monitoring'; import { buildBulkBody } from '../factories/utils/build_bulk_body'; import type { SignalSource } from '../types'; +import { generateAlertId } from './utils'; export const wrapEsqlAlerts = ({ - results, + events, spaceId, completeRule, mergeStrategy, @@ -29,12 +29,10 @@ export const wrapEsqlAlerts = ({ ruleExecutionLogger, publicBaseUrl, tuple, - sourceDocuments, isRuleAggregating, }: { isRuleAggregating: boolean; - sourceDocuments: Record; - results: Array>; + events: Array>; spaceId: string | null | undefined; completeRule: CompleteRule; mergeStrategy: ConfigType['alertMergeStrategy']; @@ -47,37 +45,20 @@ export const wrapEsqlAlerts = ({ maxSignals: number; }; }): Array> => { - const wrapped = results.map>((document, i) => { - const ruleRunId = tuple.from.toISOString() + tuple.to.toISOString(); - - // for aggregating rules when metadata _id is present, generate alert based on ES document event id - const id = - !isRuleAggregating && document._id - ? objectHash([ - document._id, - document._version, - document._index, - `${spaceId}:${completeRule.alertId}`, - ]) - : objectHash([ - ruleRunId, - completeRule.ruleParams.query, - `${spaceId}:${completeRule.alertId}`, - i, - ]); - - // metadata fields need to be excluded from source, otherwise alerts creation fails - const { _id, _version, _index, ...source } = document; + const wrapped = events.map>((event, i) => { + const id = generateAlertId({ + event, + spaceId, + completeRule, + tuple, + isRuleAggregating, + index: i, + }); const baseAlert: BaseFieldsLatest = buildBulkBody( spaceId, completeRule, - { - _source: source as SignalSource, - fields: _id ? sourceDocuments[_id]?.fields : undefined, - _id: _id ?? '', - _index: _index ?? '', - }, + event, mergeStrategy, [], true, @@ -91,7 +72,7 @@ export const wrapEsqlAlerts = ({ return { _id: id, - _index: _index ?? '', + _index: event._index ?? '', _source: { ...baseAlert, }, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/wrap_suppressed_esql_alerts.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/wrap_suppressed_esql_alerts.test.ts new file mode 100644 index 0000000000000..0c3c910efa056 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/wrap_suppressed_esql_alerts.test.ts @@ -0,0 +1,151 @@ +/* + * Copyright 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 { cloneDeep } from 'lodash'; +import moment from 'moment'; + +import { + ALERT_URL, + ALERT_UUID, + ALERT_SUPPRESSION_DOCS_COUNT, + ALERT_INSTANCE_ID, + ALERT_SUPPRESSION_TERMS, + ALERT_SUPPRESSION_START, + ALERT_SUPPRESSION_END, +} from '@kbn/rule-data-utils'; +import { getCompleteRuleMock, getEsqlRuleParams } from '../../rule_schema/mocks'; +import { ruleExecutionLogMock } from '../../rule_monitoring/mocks'; +import { sampleDocNoSortIdWithTimestamp } from '../__mocks__/es_results'; +import { wrapSuppressedEsqlAlerts } from './wrap_suppressed_esql_alerts'; + +import * as esqlUtils from './utils/generate_alert_id'; + +const ruleExecutionLogger = ruleExecutionLogMock.forExecutors.create(); + +const docId = 'd5e8eb51-a6a0-456d-8a15-4b79bfec3d71'; +const publicBaseUrl = 'http://somekibanabaseurl.com'; + +const alertSuppression = { + groupBy: ['source.ip'], +}; + +const completeRule = getCompleteRuleMock(getEsqlRuleParams()); +completeRule.ruleParams.alertSuppression = alertSuppression; + +describe('wrapSuppressedEsqlAlerts', () => { + test('should create an alert with the correct _id from a document and suppression fields', () => { + const doc = sampleDocNoSortIdWithTimestamp(docId); + const alerts = wrapSuppressedEsqlAlerts({ + events: [doc], + isRuleAggregating: false, + spaceId: 'default', + mergeStrategy: 'missingFields', + completeRule, + alertTimestampOverride: undefined, + ruleExecutionLogger, + publicBaseUrl, + primaryTimestamp: '@timestamp', + tuple: { + to: moment('2010-10-20 04:43:12'), + from: moment('2010-10-20 04:43:12'), + maxSignals: 100, + }, + }); + + expect(alerts[0]._id).toEqual('d94fb11e6062d7dce881ea07d952a1280398663a'); + expect(alerts[0]._source[ALERT_UUID]).toEqual('d94fb11e6062d7dce881ea07d952a1280398663a'); + expect(alerts[0]._source[ALERT_URL]).toContain( + 'http://somekibanabaseurl.com/app/security/alerts/redirect/d94fb11e6062d7dce881ea07d952a1280398663a?index=.alerts-security.alerts-default' + ); + expect(alerts[0]._source[ALERT_SUPPRESSION_DOCS_COUNT]).toEqual(0); + expect(alerts[0]._source[ALERT_INSTANCE_ID]).toEqual( + '1bf77f90e72d76d9335ad0ce356340a3d9833f96' + ); + expect(alerts[0]._source[ALERT_SUPPRESSION_TERMS]).toEqual([ + { field: 'source.ip', value: ['127.0.0.1'] }, + ]); + expect(alerts[0]._source[ALERT_SUPPRESSION_START]).toBeDefined(); + expect(alerts[0]._source[ALERT_SUPPRESSION_END]).toBeDefined(); + }); + + test('should create an alert with a different _id if suppression field is different', () => { + const completeRuleCloned = cloneDeep(completeRule); + completeRuleCloned.ruleParams.alertSuppression = { + groupBy: ['someKey'], + }; + const doc = sampleDocNoSortIdWithTimestamp(docId); + const alerts = wrapSuppressedEsqlAlerts({ + events: [doc], + spaceId: 'default', + isRuleAggregating: true, + mergeStrategy: 'missingFields', + completeRule: completeRuleCloned, + alertTimestampOverride: undefined, + ruleExecutionLogger, + publicBaseUrl, + primaryTimestamp: '@timestamp', + tuple: { + to: moment('2010-10-20 04:43:12'), + from: moment('2010-10-20 04:43:12'), + maxSignals: 100, + }, + }); + + expect(alerts[0]._source[ALERT_URL]).toContain( + 'http://somekibanabaseurl.com/app/security/alerts/redirect/' + ); + expect(alerts[0]._source[ALERT_SUPPRESSION_DOCS_COUNT]).toEqual(0); + expect(alerts[0]._source[ALERT_INSTANCE_ID]).toEqual( + 'c88edd552cb3501f040aea63ec68312e71af2ed2' + ); + expect(alerts[0]._source[ALERT_SUPPRESSION_TERMS]).toEqual([ + { field: 'someKey', value: 'someValue' }, + ]); + }); + + test('should call generateAlertId for alert id', () => { + jest.spyOn(esqlUtils, 'generateAlertId').mockReturnValueOnce('mocked-alert-id'); + const completeRuleCloned = cloneDeep(completeRule); + completeRuleCloned.ruleParams.alertSuppression = { + groupBy: ['someKey'], + }; + const doc = sampleDocNoSortIdWithTimestamp(docId); + const alerts = wrapSuppressedEsqlAlerts({ + events: [doc], + spaceId: 'default', + isRuleAggregating: false, + mergeStrategy: 'missingFields', + completeRule: completeRuleCloned, + alertTimestampOverride: undefined, + ruleExecutionLogger, + publicBaseUrl, + primaryTimestamp: '@timestamp', + tuple: { + to: moment('2010-10-20 04:43:12'), + from: moment('2010-10-20 04:43:12'), + maxSignals: 100, + }, + }); + + expect(alerts[0]._id).toEqual('mocked-alert-id'); + expect(alerts[0]._source[ALERT_UUID]).toEqual('mocked-alert-id'); + + expect(esqlUtils.generateAlertId).toHaveBeenCalledWith( + expect.objectContaining({ + completeRule: expect.any(Object), + event: expect.any(Object), + index: 0, + isRuleAggregating: false, + spaceId: 'default', + tuple: { + from: expect.any(Object), + maxSignals: 100, + to: expect.any(Object), + }, + }) + ); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/wrap_suppressed_esql_alerts.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/wrap_suppressed_esql_alerts.ts new file mode 100644 index 0000000000000..057cd5c906167 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/wrap_suppressed_esql_alerts.ts @@ -0,0 +1,111 @@ +/* + * Copyright 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 objectHash from 'object-hash'; +import type { Moment } from 'moment'; +import type * as estypes from '@elastic/elasticsearch/lib/api/types'; +import { TIMESTAMP } from '@kbn/rule-data-utils'; +import type { SuppressionFieldsLatest } from '@kbn/rule-registry-plugin/common/schemas'; + +import type { + BaseFieldsLatest, + WrappedFieldsLatest, +} from '../../../../../common/api/detection_engine/model/alerts'; +import type { ConfigType } from '../../../../config'; +import type { CompleteRule, EsqlRuleParams } from '../../rule_schema'; +import { buildReasonMessageForNewTermsAlert } from '../utils/reason_formatters'; +import type { IRuleExecutionLogForExecutors } from '../../rule_monitoring'; +import { buildBulkBody } from '../factories/utils/build_bulk_body'; +import type { SignalSource } from '../types'; +import { getSuppressionAlertFields, getSuppressionTerms } from '../utils'; +import { generateAlertId } from './utils'; + +export const wrapSuppressedEsqlAlerts = ({ + events, + spaceId, + completeRule, + mergeStrategy, + alertTimestampOverride, + ruleExecutionLogger, + publicBaseUrl, + tuple, + isRuleAggregating, + primaryTimestamp, + secondaryTimestamp, +}: { + isRuleAggregating: boolean; + events: Array>; + spaceId: string | null | undefined; + completeRule: CompleteRule; + mergeStrategy: ConfigType['alertMergeStrategy']; + alertTimestampOverride: Date | undefined; + ruleExecutionLogger: IRuleExecutionLogForExecutors; + publicBaseUrl: string | undefined; + tuple: { + to: Moment; + from: Moment; + maxSignals: number; + }; + primaryTimestamp: string; + secondaryTimestamp?: string; +}): Array> => { + const wrapped = events.map>( + (event, i) => { + const combinedFields = { ...event?.fields, ...event._source }; + + const suppressionTerms = getSuppressionTerms({ + alertSuppression: completeRule?.ruleParams?.alertSuppression, + fields: combinedFields, + }); + + const id = generateAlertId({ + event, + spaceId, + completeRule, + tuple, + isRuleAggregating, + index: i, + suppressionTerms, + }); + + const instanceId = objectHash([suppressionTerms, completeRule.alertId, spaceId]); + + const baseAlert: BaseFieldsLatest = buildBulkBody( + spaceId, + completeRule, + event, + mergeStrategy, + [], + true, + buildReasonMessageForNewTermsAlert, + [], + alertTimestampOverride, + ruleExecutionLogger, + id, + publicBaseUrl + ); + + return { + _id: id, + _index: event._index ?? '', + _source: { + ...baseAlert, + ...getSuppressionAlertFields({ + primaryTimestamp, + secondaryTimestamp, + fields: combinedFields, + suppressionTerms, + fallbackTimestamp: baseAlert[TIMESTAMP], + instanceId, + }), + }, + }; + } + ); + + return wrapped; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/strip_non_ecs_fields.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/strip_non_ecs_fields.ts index 86a3dc4ff6c1f..08e5fa5fd879d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/strip_non_ecs_fields.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/strip_non_ecs_fields.ts @@ -67,9 +67,9 @@ const getIsEcsFieldObject = (path: string) => { /** * checks if path is in Ecs mapping */ -const getIsEcsField = (path: string) => { +export const getIsEcsField = (path: string): boolean => { const ecsField = ecsFieldMap[path as keyof typeof ecsFieldMap]; - const isEcsField = !!ecsField || ecsObjectFields[path]; + const isEcsField = Boolean(!!ecsField || ecsObjectFields[path]); return isEcsField; }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/bulk_create_suppressed_alerts_in_memory.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/bulk_create_suppressed_alerts_in_memory.ts index 2fffa07f8d684..efa8e95c522a5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/bulk_create_suppressed_alerts_in_memory.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/bulk_create_suppressed_alerts_in_memory.ts @@ -87,7 +87,7 @@ export const bulkCreateSuppressedNewTermsAlertsInMemory = async ({ const partitionedEvents = partitionMissingFieldsEvents( eventsAndTerms, alertSuppression?.groupBy || [], - ['event'] + ['event', 'fields'] ); unsuppressibleWrappedDocs = wrapHits(partitionedEvents[1]); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/bulk_create_suppressed_alerts_in_memory.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/bulk_create_suppressed_alerts_in_memory.ts index 85bfc76e964e2..030cb213d94dd 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/bulk_create_suppressed_alerts_in_memory.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/bulk_create_suppressed_alerts_in_memory.ts @@ -51,6 +51,8 @@ export interface BulkCreateSuppressedAlertsParams enrichedEvents: SignalSourceHit[]; toReturn: SearchAfterAndBulkCreateReturnType; experimentalFeatures: ExperimentalFeatures; + mergeSourceAndFields?: boolean; + maxNumberOfAlertsMultiplier?: number; } /** * wraps, bulk create and suppress alerts in memory, also takes care of missing fields logic. @@ -70,6 +72,8 @@ export const bulkCreateSuppressedAlertsInMemory = async ({ alertWithSuppression, alertTimestampOverride, experimentalFeatures, + mergeSourceAndFields = false, + maxNumberOfAlertsMultiplier, }: BulkCreateSuppressedAlertsParams) => { const suppressOnMissingFields = (alertSuppression?.missingFieldsStrategy ?? DEFAULT_SUPPRESSION_MISSING_FIELDS_STRATEGY) === @@ -81,7 +85,9 @@ export const bulkCreateSuppressedAlertsInMemory = async ({ if (!suppressOnMissingFields) { const partitionedEvents = partitionMissingFieldsEvents( enrichedEvents, - alertSuppression?.groupBy || [] + alertSuppression?.groupBy || [], + ['fields'], + mergeSourceAndFields ); unsuppressibleWrappedDocs = wrapHits(partitionedEvents[1], buildReasonMessage); @@ -102,6 +108,7 @@ export const bulkCreateSuppressedAlertsInMemory = async ({ alertWithSuppression, alertTimestampOverride, experimentalFeatures, + maxNumberOfAlertsMultiplier, }); }; @@ -120,6 +127,7 @@ export interface ExecuteBulkCreateAlertsParams>; toReturn: SearchAfterAndBulkCreateReturnType; experimentalFeatures: ExperimentalFeatures; + maxNumberOfAlertsMultiplier?: number; } /** @@ -139,11 +147,12 @@ export const executeBulkCreateAlerts = async < alertWithSuppression, alertTimestampOverride, experimentalFeatures, + maxNumberOfAlertsMultiplier = MAX_SIGNALS_SUPPRESSION_MULTIPLIER, }: ExecuteBulkCreateAlertsParams) => { // max signals for suppression includes suppressed and created alerts // this allows to lift max signals limitation to higher value // and can detects events beyond default max_signals value - const suppressionMaxSignals = MAX_SIGNALS_SUPPRESSION_MULTIPLIER * tuple.maxSignals; + const suppressionMaxSignals = maxNumberOfAlertsMultiplier * tuple.maxSignals; const suppressionDuration = alertSuppression?.duration; const suppressionWindow = suppressionDuration diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/partition_missing_fields_events.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/partition_missing_fields_events.test.ts index dfee32a058ba6..7fad1d4f2b10c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/partition_missing_fields_events.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/partition_missing_fields_events.test.ts @@ -30,7 +30,8 @@ describe('partitionMissingFieldsEvents', () => { _index: 'index-0', }, ], - ['agent.host', 'agent.type', 'agent.version'] + ['agent.host', 'agent.type', 'agent.version'], + ['fields'] ) ).toEqual([ [ @@ -83,7 +84,7 @@ describe('partitionMissingFieldsEvents', () => { }, ], ['agent.host', 'agent.type', 'agent.version'], - ['event'] + ['event', 'fields'] ) ).toEqual([ [ @@ -113,6 +114,35 @@ describe('partitionMissingFieldsEvents', () => { ], ]); }); + it('should partition when fields located in root of event', () => { + expect( + partitionMissingFieldsEvents( + [ + { + 'agent.host': 'host-1', + 'agent.version': 2, + }, + { + 'agent.host': 'host-1', + }, + ], + ['agent.host', 'agent.version'], + [] + ) + ).toEqual([ + [ + { + 'agent.host': 'host-1', + 'agent.version': 2, + }, + ], + [ + { + 'agent.host': 'host-1', + }, + ], + ]); + }); it('should partition if two fields are empty', () => { expect( partitionMissingFieldsEvents( @@ -125,7 +155,8 @@ describe('partitionMissingFieldsEvents', () => { _index: 'index-0', }, ], - ['agent.host', 'agent.type', 'agent.version'] + ['agent.host', 'agent.type', 'agent.version'], + ['fields'] ) ).toEqual([ [], @@ -152,7 +183,8 @@ describe('partitionMissingFieldsEvents', () => { _index: 'index-0', }, ], - ['agent.host', 'agent.type', 'agent.version'] + ['agent.host', 'agent.type', 'agent.version'], + ['fields'] ) ).toEqual([ [], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/partition_missing_fields_events.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/partition_missing_fields_events.ts index f86a969dc60c3..901768fe5c773 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/partition_missing_fields_events.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/partition_missing_fields_events.ts @@ -17,20 +17,25 @@ import type { SignalSourceHit } from '../types'; * 2. where any of fields is empty */ export const partitionMissingFieldsEvents = < - T extends SignalSourceHit | { event: SignalSourceHit } + T extends SignalSourceHit | { event: SignalSourceHit } | Record >( events: T[], suppressedBy: string[] = [], // path to fields property within event object. At this point, it can be in root of event object or within event key - fieldsPath: ['event'] | [] = [] + fieldsPath: ['event', 'fields'] | ['fields'] | [] = [], + mergeSourceAndFields: boolean = false ): T[][] => { return partition(events, (event) => { if (suppressedBy.length === 0) { return true; } - const eventFields = get(event, [...fieldsPath, 'fields']); - const hasMissingFields = - Object.keys(pick(eventFields, suppressedBy)).length < suppressedBy.length; + const eventFields = fieldsPath.length ? get(event, fieldsPath) : event; + const sourceFields = + (event as SignalSourceHit)?._source || (event as { event: SignalSourceHit })?.event?._source; + + const fields = mergeSourceAndFields ? { ...sourceFields, ...eventFields } : eventFields; + + const hasMissingFields = Object.keys(pick(fields, suppressedBy)).length < suppressedBy.length; return !hasMissingFields; }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/suppression_utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/suppression_utils.ts index 167f22dc1d52b..44febba73e68e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/suppression_utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/suppression_utils.ts @@ -17,7 +17,8 @@ import { ALERT_SUPPRESSION_END, } from '@kbn/rule-data-utils'; import type { AlertSuppressionCamel } from '../../../../../common/api/detection_engine/model/rule_schema'; -interface SuppressionTerm { + +export interface SuppressionTerm { field: string; value: string[] | number[] | null; } @@ -33,7 +34,7 @@ export const getSuppressionAlertFields = ({ fallbackTimestamp, instanceId, }: { - fields: Record | undefined; + fields: Record | undefined; primaryTimestamp: string; secondaryTimestamp?: string; suppressionTerms: SuppressionTerm[]; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.ts index 3c930ec07e666..9f99a9ae4a561 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.ts @@ -13,10 +13,7 @@ import type { import type { ElasticsearchClient, Logger } from '@kbn/core/server'; import { ALERT_RISK_SCORE, - ALERT_RULE_NAME, - ALERT_UUID, ALERT_WORKFLOW_STATUS, - EVENT_KIND, } from '@kbn/rule-registry-plugin/common/technical_rule_data_field_names'; import type { RiskScoresPreviewResponse } from '../../../../common/api/entity_analytics/risk_engine/preview_route.gen'; import type { @@ -28,6 +25,7 @@ import { type IdentifierType, getRiskLevel, RiskCategories, + RiskWeightTypes, } from '../../../../common/entity_analytics/risk_engine'; import { withSecuritySpan } from '../../../utils/with_security_span'; import type { AssetCriticalityRecord } from '../../../../common/api/entity_analytics'; @@ -38,24 +36,13 @@ import { normalize, } from '../asset_criticality/helpers'; import { getAfterKeyForIdentifierType, getFieldForIdentifier } from './helpers'; -import { - buildCategoryCountDeclarations, - buildCategoryAssignment, - buildCategoryScoreDeclarations, - buildWeightingOfScoreByCategory, - getGlobalWeightForIdentifierType, -} from './risk_weights'; import type { CalculateRiskScoreAggregations, CalculateScoresParams, RiskScoreBucket, } from '../types'; -import { - MAX_INPUTS_COUNT, - RISK_SCORING_INPUTS_COUNT_MAX, - RISK_SCORING_SUM_MAX, - RISK_SCORING_SUM_VALUE, -} from './constants'; +import { RISK_SCORING_SUM_MAX, RISK_SCORING_SUM_VALUE } from './constants'; +import { getPainlessScripts, type PainlessScripts } from './painless'; const formatForResponse = ({ bucket, @@ -118,67 +105,22 @@ const filterFromRange = (range: CalculateScoresParams['range']): QueryDslQueryCo range: { '@timestamp': { lt: range.end, gte: range.start } }, }); -const buildReduceScript = ({ - globalIdentifierTypeWeight, -}: { - globalIdentifierTypeWeight?: number; -}): string => { - return ` - Map results = new HashMap(); - List inputs = []; - for (state in states) { - inputs.addAll(state.inputs) - } - Collections.sort(inputs, (a, b) -> b.get('weighted_score').compareTo(a.get('weighted_score'))); - - double num_inputs_to_score = Math.min(inputs.length, params.max_risk_inputs_per_identity); - results['notes'] = []; - if (num_inputs_to_score == params.max_risk_inputs_per_identity) { - results['notes'].add('Number of risk inputs (' + inputs.length + ') exceeded the maximum allowed (' + params.max_risk_inputs_per_identity + ').'); - } - - ${buildCategoryScoreDeclarations()} - ${buildCategoryCountDeclarations()} - - double total_score = 0; - double current_score = 0; - List risk_inputs = []; - for (int i = 0; i < num_inputs_to_score; i++) { - current_score = inputs[i].weighted_score / Math.pow(i + 1, params.p); - - if (i < ${MAX_INPUTS_COUNT}) { - inputs[i]["contribution"] = 100 * current_score / params.risk_cap; - risk_inputs.add(inputs[i]); - } - - ${buildCategoryAssignment()} - total_score += current_score; - } - - ${globalIdentifierTypeWeight != null ? `total_score *= ${globalIdentifierTypeWeight};` : ''} - double score_norm = 100 * total_score / params.risk_cap; - results['score'] = total_score; - results['normalized_score'] = score_norm; - results['risk_inputs'] = risk_inputs; - - return results; - `; -}; - const buildIdentifierTypeAggregation = ({ afterKeys, identifierType, pageSize, weights, alertSampleSizePerShard, + scriptedMetricPainless, }: { afterKeys: AfterKeys; identifierType: IdentifierType; pageSize: number; weights?: RiskScoreWeights; alertSampleSizePerShard: number; + scriptedMetricPainless: PainlessScripts; }): AggregationsAggregationContainer => { - const globalIdentifierTypeWeight = getGlobalWeightForIdentifierType({ identifierType, weights }); + const globalIdentifierTypeWeight = getGlobalWeightForIdentifierType(identifierType, weights); const identifierField = getFieldForIdentifier(identifierType); return { @@ -204,33 +146,15 @@ const buildIdentifierTypeAggregation = ({ aggs: { risk_details: { scripted_metric: { - init_script: 'state.inputs = []', - map_script: ` - Map fields = new HashMap(); - String category = doc['${EVENT_KIND}'].value; - double score = doc['${ALERT_RISK_SCORE}'].value; - double weighted_score = 0.0; - - fields.put('time', doc['@timestamp'].value); - fields.put('rule_name', doc['${ALERT_RULE_NAME}'].value); - - fields.put('category', category); - fields.put('index', doc['_index'].value); - fields.put('id', doc['${ALERT_UUID}'].value); - fields.put('score', score); - - ${buildWeightingOfScoreByCategory({ userWeights: weights, identifierType })} - fields.put('weighted_score', weighted_score); - - state.inputs.add(fields); - `, - combine_script: 'return state;', + init_script: scriptedMetricPainless.init, + map_script: scriptedMetricPainless.map, + combine_script: scriptedMetricPainless.combine, params: { - max_risk_inputs_per_identity: RISK_SCORING_INPUTS_COUNT_MAX, p: RISK_SCORING_SUM_VALUE, risk_cap: RISK_SCORING_SUM_MAX, + global_identifier_type_weight: globalIdentifierTypeWeight || 1, }, - reduce_script: buildReduceScript({ globalIdentifierTypeWeight }), + reduce_script: scriptedMetricPainless.reduce, }, }, }, @@ -286,6 +210,12 @@ const processScores = async ({ }); }; +export const getGlobalWeightForIdentifierType = ( + identifierType: IdentifierType, + weights?: RiskScoreWeights +): number | undefined => + weights?.find((weight) => weight.type === RiskWeightTypes.global)?.[identifierType]; + export const calculateRiskScores = async ({ afterKeys: userAfterKeys, assetCriticalityService, @@ -307,6 +237,7 @@ export const calculateRiskScores = async ({ } & CalculateScoresParams): Promise => withSecuritySpan('calculateRiskScores', async () => { const now = new Date().toISOString(); + const scriptedMetricPainless = await getPainlessScripts(); const filter = [ filterFromRange(range), { bool: { must_not: { term: { [ALERT_WORKFLOW_STATUS]: 'closed' } } } }, @@ -345,6 +276,7 @@ export const calculateRiskScores = async ({ pageSize, weights, alertSampleSizePerShard, + scriptedMetricPainless, }); return aggs; }, {} as Record), diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/constants.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/constants.ts index 73f93d71b11f9..6a691eac42734 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/constants.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/constants.ts @@ -16,11 +16,6 @@ export const RISK_SCORING_SUM_VALUE = 1.5; */ export const RISK_SCORING_SUM_MAX = 261.2; -/** - * The risk scoring algorithm can only process a finite number of risk inputs per identity; this value represents the maximum number of inputs that will be processed. - */ -export const RISK_SCORING_INPUTS_COUNT_MAX = 999999; - /** * This value represents the maximum possible risk score after normalization. */ diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/index.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/index.test.ts new file mode 100644 index 0000000000000..e21a6afeff326 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/index.test.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getPainlessScripts } from '.'; + +describe('getPainlessScripts', () => { + // to update snapshot run `yarn test:jest x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/index.test.ts -u` + test('Scripts should not have changed. If this change is intentional, ensure that Serverless scripted metric allowlists are updated', async () => { + const scripts = await getPainlessScripts(); + + expect(scripts).toMatchInlineSnapshot(` + Object { + "combine": "return state;", + "init": "state.inputs = []", + "map": "Map fields = new HashMap();fields.put('id', doc['kibana.alert.uuid'].value);fields.put('index', doc['_index'].value);fields.put('time', doc['@timestamp'].value);fields.put('rule_name', doc['kibana.alert.rule.name'].value);fields.put('category', doc['event.kind'].value);fields.put('score', doc['kibana.alert.risk_score'].value);state.inputs.add(fields); ", + "reduce": "Map results = new HashMap();results['notes'] = [];results['category_1_score'] = 0.0;results['category_1_count'] = 0;results['risk_inputs'] = [];results['score'] = 0.0;def inputs = states[0].inputs;Collections.sort(inputs, (a, b) -> b.get('score').compareTo(a.get('score')));for (int i = 0; i < inputs.length; i++) { double current_score = inputs[i].score / Math.pow(i + 1, params.p); if (i < 10) { inputs[i][\\"contribution\\"] = 100 * current_score / params.risk_cap; results['risk_inputs'].add(inputs[i]); } results['category_1_score'] += current_score; results['category_1_count'] += 1; results['score'] += current_score;}results['score'] *= params.global_identifier_type_weight;results['normalized_score'] = 100 * results['score'] / params.risk_cap;return results;", + } + `); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/index.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/index.ts new file mode 100644 index 0000000000000..896340262b21c --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/index.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 fs from 'fs'; +import { flow } from 'lodash'; + +const PHASES = ['init', 'map', 'combine', 'reduce'] as const; + +type Phase = typeof PHASES[number]; +export type PainlessScripts = Record; + +const removeNewlines = (content: string) => content.replace(/\n/g, ''); +const condenseMultipleSpaces = (content: string) => content.replace(/\s+/g, ' '); +const removeComments = (content: string) => content.replace(/\/\/.*/g, ''); +const minifyContent = flow(removeComments, removeNewlines, condenseMultipleSpaces); + +const readScript = async (phase: Phase) => { + const content = await fs.promises.readFile(`${__dirname}/risk_scoring_${phase}.painless`, 'utf8'); + return minifyContent(content); +}; + +let cache: PainlessScripts | undefined; + +export const getPainlessScripts = async (): Promise => { + if (cache) { + return cache; + } + + const [init, map, combine, reduce] = await Promise.all(PHASES.map(readScript)); + + // The cache will only ever have one value, so we can safely update it + // un-atomicly without worrying about lost updates. + // eslint-disable-next-line require-atomic-updates + cache = { init, map, combine, reduce }; + return cache; +}; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_combine.painless b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_combine.painless new file mode 100644 index 0000000000000..da2a75d569f18 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_combine.painless @@ -0,0 +1 @@ +return state; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_init.painless b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_init.painless new file mode 100644 index 0000000000000..5ee9376d701ad --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_init.painless @@ -0,0 +1 @@ +state.inputs = [] diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_map.painless b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_map.painless new file mode 100644 index 0000000000000..3d79df51be0e8 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_map.painless @@ -0,0 +1,8 @@ +Map fields = new HashMap(); +fields.put('id', doc['kibana.alert.uuid'].value); +fields.put('index', doc['_index'].value); +fields.put('time', doc['@timestamp'].value); +fields.put('rule_name', doc['kibana.alert.rule.name'].value); +fields.put('category', doc['event.kind'].value); +fields.put('score', doc['kibana.alert.risk_score'].value); +state.inputs.add(fields); \ No newline at end of file diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_reduce.painless b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_reduce.painless new file mode 100644 index 0000000000000..629a925522590 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_reduce.painless @@ -0,0 +1,41 @@ +Map results = new HashMap(); +results['notes'] = []; +results['category_1_score'] = 0.0; +results['category_1_count'] = 0; +results['risk_inputs'] = []; +results['score'] = 0.0; + +def inputs = states[0].inputs; + +// Currently the alerts index only has one shard so there will only be one state +// If there are multiple shards we will need these lines +// List inputs = []; +// for (state in states) { +// inputs.addAll(state.inputs) +// } + +// sorting is needed even though we sort in the parent query because the scripted metric +// agg does not guarantee order. +Collections.sort(inputs, (a, b) -> b.get('score').compareTo(a.get('score'))); + +for (int i = 0; i < inputs.length; i++) { + double current_score = inputs[i].score / Math.pow(i + 1, params.p); + + if (i < 10) { + inputs[i]["contribution"] = 100 * current_score / params.risk_cap; + results['risk_inputs'].add(inputs[i]); + } + +// every input is of type signal at the moment +// if (inputs[i].category == 'signal') { + results['category_1_score'] += current_score; + results['category_1_count'] += 1; +// } + + results['score'] += current_score; +} + +results['score'] *= params.global_identifier_type_weight; +results['normalized_score'] = 100 * results['score'] / params.risk_cap; + +return results; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.test.ts deleted file mode 100644 index 86bdc0d0e6be0..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.test.ts +++ /dev/null @@ -1,123 +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 { RiskWeightTypes, RiskCategories } from '../../../../common/entity_analytics/risk_engine'; -import { - buildCategoryAssignment, - buildCategoryWeights, - buildWeightingOfScoreByCategory, -} from './risk_weights'; - -describe('buildCategoryWeights', () => { - it('returns the default weights if nothing else is provided', () => { - const result = buildCategoryWeights(); - - expect(result).toEqual([ - { host: 1, type: RiskWeightTypes.riskCategory, user: 1, value: RiskCategories.category_1 }, - ]); - }); - - it('allows user weights to override defaults', () => { - const result = buildCategoryWeights([ - { - type: RiskWeightTypes.riskCategory, - value: RiskCategories.category_1, - host: 0.1, - user: 0.2, - }, - ]); - - expect(result).toEqual([ - { - host: 0.1, - type: RiskWeightTypes.riskCategory, - user: 0.2, - value: RiskCategories.category_1, - }, - ]); - }); - - it('uses default category weights if unspecified in user-provided weight', () => { - const result = buildCategoryWeights([ - { type: RiskWeightTypes.riskCategory, value: RiskCategories.category_1, host: 0.1 }, - ]); - - expect(result).toEqual([ - { host: 0.1, type: RiskWeightTypes.riskCategory, user: 1, value: RiskCategories.category_1 }, - ]); - }); -}); - -describe('buildCategoryAssignment', () => { - it('builds the expected assignment statement', () => { - const result = buildCategoryAssignment(); - - expect(result).toMatchInlineSnapshot( - `"if (inputs[i].category == 'signal') { results['category_1_score'] += current_score; results['category_1_count'] += 1; }"` - ); - }); -}); - -describe('buildWeightingOfScoreByCategory', () => { - it('returns default weights if no user values provided', () => { - const result = buildWeightingOfScoreByCategory({ identifierType: 'user' }); - - expect(result).toMatchInlineSnapshot( - `"if (category == 'signal') { weighted_score = score * 1; } else { weighted_score = score; }"` - ); - }); - - it('returns default weights if no weights provided', () => { - const result = buildWeightingOfScoreByCategory({ userWeights: [], identifierType: 'host' }); - - expect(result).toMatchInlineSnapshot( - `"if (category == 'signal') { weighted_score = score * 1; } else { weighted_score = score; }"` - ); - }); - - it('returns default weights if only global weights provided', () => { - const result = buildWeightingOfScoreByCategory({ - userWeights: [{ type: RiskWeightTypes.global, host: 0.1 }], - identifierType: 'host', - }); - - expect(result).toMatchInlineSnapshot( - `"if (category == 'signal') { weighted_score = score * 1; } else { weighted_score = score; }"` - ); - }); - - it('returns specified weight when a category weight is provided', () => { - const result = buildWeightingOfScoreByCategory({ - userWeights: [ - { - type: RiskWeightTypes.riskCategory, - value: RiskCategories.category_1, - host: 0.1, - user: 0.2, - }, - ], - identifierType: 'host', - }); - - expect(result).toMatchInlineSnapshot( - `"if (category == 'signal') { weighted_score = score * 0.1; } else { weighted_score = score; }"` - ); - }); - - it('returns a default weight when a category weight is provided but not the one being used', () => { - const result = buildWeightingOfScoreByCategory({ - userWeights: [ - { type: RiskWeightTypes.riskCategory, value: RiskCategories.category_1, host: 0.1 }, - ], - identifierType: 'user', - }); - - expect(result).toMatchInlineSnapshot( - `"if (category == 'signal') { weighted_score = score * 1; } else { weighted_score = score; }"` - ); - }); -}); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.ts deleted file mode 100644 index d0c7486324e30..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.ts +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { keyBy, merge } from 'lodash'; -import type { - RiskScoreWeight, - RiskScoreWeightCategory, - RiskScoreWeightGlobal, - RiskScoreWeights, -} from '../../../../common/api/entity_analytics/common'; -import type { IdentifierType } from '../../../../common/entity_analytics/risk_engine'; -import { RiskCategories, RiskWeightTypes } from '../../../../common/entity_analytics/risk_engine'; - -const RISK_CATEGORIES = Object.values(RiskCategories); - -const DEFAULT_CATEGORY_WEIGHTS: RiskScoreWeights = RISK_CATEGORIES.map((category) => ({ - type: RiskWeightTypes.riskCategory, - value: category, - host: 1, - user: 1, -})); - -/* - * This function and its use can be deleted once we've replaced our use of event.kind with a proper risk category field. - */ -const convertCategoryToEventKindValue = (category?: string): string | undefined => - category === 'category_1' ? 'signal' : category; - -const isGlobalIdentifierTypeWeight = (weight: RiskScoreWeight): weight is RiskScoreWeightGlobal => - weight.type === RiskWeightTypes.global; -const isRiskCategoryWeight = (weight: RiskScoreWeight): weight is RiskScoreWeightCategory => - weight.type === RiskWeightTypes.riskCategory; - -export const getGlobalWeightForIdentifierType = ({ - identifierType, - weights, -}: { - identifierType: IdentifierType; - weights?: RiskScoreWeights; -}): number | undefined => { - return weights?.find(isGlobalIdentifierTypeWeight)?.[identifierType]; -}; - -const getRiskCategoryWeights = (weights?: RiskScoreWeights): RiskScoreWeightCategory[] => - weights?.filter(isRiskCategoryWeight) ?? []; - -const getWeightForIdentifierType = ( - weight: RiskScoreWeight, - identifierType: IdentifierType -): number => { - const configuredWeight = weight[identifierType]; - return typeof configuredWeight === 'number' ? configuredWeight : 1; -}; - -export const buildCategoryScoreDeclarations = (): string => { - return RISK_CATEGORIES.map((riskCategory) => `results['${riskCategory}_score'] = 0.0;`).join(''); -}; - -export const buildCategoryCountDeclarations = (): string => { - return RISK_CATEGORIES.map((riskCategory) => `results['${riskCategory}_count'] = 0;`).join(''); -}; - -export const buildCategoryWeights = (userWeights?: RiskScoreWeights): RiskScoreWeightCategory[] => { - const categoryWeights = getRiskCategoryWeights(userWeights); - - return Object.values( - merge({}, keyBy(DEFAULT_CATEGORY_WEIGHTS, 'value'), keyBy(categoryWeights, 'value')) - ); -}; - -export const buildCategoryAssignment = (): string => { - return RISK_CATEGORIES.map( - (category) => - `if (inputs[i].category == '${convertCategoryToEventKindValue( - category - )}') { results['${category}_score'] += current_score; results['${category}_count'] += 1; }` - ).join(' else '); -}; - -export const buildWeightingOfScoreByCategory = ({ - userWeights, - identifierType, -}: { - userWeights?: RiskScoreWeights; - identifierType: IdentifierType; -}): string => { - const otherClause = `weighted_score = score;`; - const categoryWeights = buildCategoryWeights(userWeights); - - return categoryWeights - .map( - (weight) => - `if (category == '${convertCategoryToEventKindValue( - weight.value - )}') { weighted_score = score * ${getWeightForIdentifierType(weight, identifierType)}; }` - ) - .join(' else ') - .concat(` else { ${otherClause} }`); -}; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.test.ts index a35f4978ebf2c..b5ff9c3487a07 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.test.ts @@ -8,10 +8,7 @@ import { loggerMock } from '@kbn/logging-mocks'; import { RISK_SCORE_PREVIEW_URL } from '../../../../../common/constants'; -import { - RiskCategories, - RiskWeightTypes, -} from '../../../../../common/entity_analytics/risk_engine'; +import { RiskWeightTypes } from '../../../../../common/entity_analytics/risk_engine'; import { serverMock, requestContextMock, @@ -171,8 +168,7 @@ describe('POST risk_engine/preview route', () => { const request = buildRequest({ weights: [ { - type: RiskWeightTypes.riskCategory, - value: RiskCategories.category_1, + type: RiskWeightTypes.global, host: 0.1, user: 0.2, }, @@ -186,8 +182,7 @@ describe('POST risk_engine/preview route', () => { expect.objectContaining({ weights: [ { - type: RiskWeightTypes.riskCategory, - value: RiskCategories.category_1, + type: RiskWeightTypes.global, host: 0.1, user: 0.2, }, @@ -200,8 +195,7 @@ describe('POST risk_engine/preview route', () => { const request = buildRequest({ weights: [ { - type: RiskWeightTypes.riskCategory, - value: RiskCategories.category_1, + type: RiskWeightTypes.global, host: 1.1, }, ], @@ -218,7 +212,6 @@ describe('POST risk_engine/preview route', () => { weights: [ { type: 'something new', - value: RiskCategories.category_1, host: 0.1, user: 0.2, }, diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts b/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts index 5b59282a320ea..f4cf62a4381b6 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts @@ -417,9 +417,7 @@ export class TelemetryReceiver implements ITelemetryReceiver { latest_metrics: { top_hits: { size: 1, - _source: { - excludes: ['*'], - }, + _source: false, sort: [ { '@timestamp': { @@ -521,6 +519,7 @@ export class TelemetryReceiver implements ITelemetryReceiver { const buckets = endpointMetadataResponse?.aggregations?.endpoint_metadata?.buckets ?? []; return buckets.reduce((cache, endpointAgentId) => { + // const id = endpointAgentId.latest_metadata.hits.hits[0]._id; const doc = endpointAgentId.latest_metadata.hits.hits[0]._source; cache.set(endpointAgentId.key, doc); return cache; diff --git a/x-pack/plugins/security_solution/server/saved_objects.ts b/x-pack/plugins/security_solution/server/saved_objects.ts index 0b1fec5677488..3659b15a04714 100644 --- a/x-pack/plugins/security_solution/server/saved_objects.ts +++ b/x-pack/plugins/security_solution/server/saved_objects.ts @@ -13,7 +13,7 @@ import { noteType, pinnedEventType, timelineType } from './lib/timeline/saved_ob import { legacyType as legacyRuleActionsType } from './lib/detection_engine/rule_actions_legacy'; import { prebuiltRuleAssetType } from './lib/detection_engine/prebuilt_rules'; import { type as signalsMigrationType } from './lib/detection_engine/migrations/saved_objects'; -import { manifestType } from './endpoint/lib/artifacts/saved_object_mappings'; +import { manifestType, unifiedManifestType } from './endpoint/lib/artifacts/saved_object_mappings'; import { riskEngineConfigurationType } from './lib/entity_analytics/risk_engine/saved_object'; const types = [ @@ -23,6 +23,7 @@ const types = [ prebuiltRuleAssetType, timelineType, manifestType, + unifiedManifestType, signalsMigrationType, riskEngineConfigurationType, protectionUpdatesNoteType, diff --git a/x-pack/plugins/security_solution/tsconfig.json b/x-pack/plugins/security_solution/tsconfig.json index b5778dbf20e39..bb26581356fa1 100644 --- a/x-pack/plugins/security_solution/tsconfig.json +++ b/x-pack/plugins/security_solution/tsconfig.json @@ -200,6 +200,7 @@ "@kbn/security-plugin-types-server", "@kbn/deeplinks-security", "@kbn/react-kibana-context-render", - "@kbn/search-types" + "@kbn/search-types", + "@kbn/field-utils" ] } diff --git a/x-pack/plugins/security_solution_serverless/server/cloud_security/cloud_security_metering_task.test.ts b/x-pack/plugins/security_solution_serverless/server/cloud_security/cloud_security_metering_task.test.ts index 9871b9e3e4a3c..205bdd5b663ea 100644 --- a/x-pack/plugins/security_solution_serverless/server/cloud_security/cloud_security_metering_task.test.ts +++ b/x-pack/plugins/security_solution_serverless/server/cloud_security/cloud_security_metering_task.test.ts @@ -203,7 +203,8 @@ describe('getSearchQueryByCloudSecuritySolution', () => { { terms: { 'resource.sub_type': [ - 'aws-ec2', + // 'aws-ebs', we can't include EBS volumes until https://github.com/elastic/security-team/issues/9283 is resolved + // 'aws-ec2', we can't include EC2 instances until https://github.com/elastic/security-team/issues/9254 is resolved 'aws-s3', 'aws-rds', 'azure-disk', @@ -213,9 +214,9 @@ describe('getSearchQueryByCloudSecuritySolution', () => { 'azure-mysql-server-db', 'azure-postgresql-server-db', 'azure-sql-server', + 'azure-storage-account', 'azure-vm', 'gcp-bigquery-dataset', - 'gcp-bigquery-table', 'gcp-compute-disk', 'gcp-compute-instance', 'gcp-sqladmin-instance', diff --git a/x-pack/plugins/security_solution_serverless/server/cloud_security/constants.ts b/x-pack/plugins/security_solution_serverless/server/cloud_security/constants.ts index 1ea6fea558dad..2a6d5825746db 100644 --- a/x-pack/plugins/security_solution_serverless/server/cloud_security/constants.ts +++ b/x-pack/plugins/security_solution_serverless/server/cloud_security/constants.ts @@ -50,7 +50,7 @@ export const BILLABLE_ASSETS_CONFIG = { filter_attribute: 'resource.sub_type', values: [ // 'aws-ebs', we can't include EBS volumes until https://github.com/elastic/security-team/issues/9283 is resolved - 'aws-ec2', + // 'aws-ec2', we can't include EC2 instances until https://github.com/elastic/security-team/issues/9254 is resolved 'aws-s3', 'aws-rds', 'azure-disk', @@ -60,9 +60,9 @@ export const BILLABLE_ASSETS_CONFIG = { 'azure-mysql-server-db', 'azure-postgresql-server-db', 'azure-sql-server', + 'azure-storage-account', 'azure-vm', 'gcp-bigquery-dataset', - 'gcp-bigquery-table', 'gcp-compute-disk', 'gcp-compute-instance', 'gcp-sqladmin-instance', diff --git a/x-pack/plugins/serverless_search/common/types/index.ts b/x-pack/plugins/serverless_search/common/types/index.ts index c2ea72f13c8e9..f4504bde81faf 100644 --- a/x-pack/plugins/serverless_search/common/types/index.ts +++ b/x-pack/plugins/serverless_search/common/types/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { IndicesIndexState, IndicesStatsIndicesStats } from '@elastic/elasticsearch/lib/api/types'; +import { IndicesIndexState } from '@elastic/elasticsearch/lib/api/types'; import { Connector } from '@kbn/search-connectors/types/connectors'; export interface CreateAPIKeyArgs { @@ -23,12 +23,10 @@ export interface IndexData { export interface FetchIndicesResult { indices: IndexData[]; } - export interface FetchIndexResult { index: IndicesIndexState & { connector?: Connector; count: number; - stats?: IndicesStatsIndicesStats; }; } diff --git a/x-pack/plugins/serverless_search/public/application/components/index_management/api_empty_prompt.tsx b/x-pack/plugins/serverless_search/public/application/components/index_management/api_empty_prompt.tsx index 4881c8d73f40f..22643b9030747 100644 --- a/x-pack/plugins/serverless_search/public/application/components/index_management/api_empty_prompt.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/index_management/api_empty_prompt.tsx @@ -26,6 +26,7 @@ import { CodeBox, getConsoleRequest, getLanguageDefinitionCodeSnippet, + IngestPipelinePanel, LanguageDefinition, LanguageDefinitionSnippetArguments, } from '@kbn/search-api-panels'; @@ -37,6 +38,10 @@ import { useKibanaServices } from '../../hooks/use_kibana'; import { javaDefinition } from '../languages/java'; import { languageDefinitions } from '../languages/languages'; import { LanguageGrid } from '../languages/language_grid'; + +import { useIngestPipelines } from '../../hooks/api/use_ingest_pipelines'; +import { DEFAULT_INGESTION_PIPELINE } from '../../constants'; + import { API_KEY_PLACEHOLDER, CLOUD_ID_PLACEHOLDER, @@ -55,6 +60,8 @@ export const APIIndexEmptyPrompt = ({ indexName, onBackClick }: APIIndexEmptyPro const assetBasePath = useAssetBasePath(); const [selectedLanguage, setSelectedLanguage] = React.useState(javaDefinition); + + const [selectedPipeline, setSelectedPipeline] = React.useState(''); const [clientApiKey, setClientApiKey] = useState(API_KEY_PLACEHOLDER); const { elasticsearchURL, cloudId } = useMemo(() => { return { @@ -67,8 +74,11 @@ export const APIIndexEmptyPrompt = ({ indexName, onBackClick }: APIIndexEmptyPro apiKey: clientApiKey, cloudId, indexName, + ingestPipeline: selectedPipeline, }; + const { data: pipelineData } = useIngestPipelines(); + const apiIngestSteps: EuiContainedStepProps[] = [ { title: i18n.translate( @@ -85,6 +95,14 @@ export const APIIndexEmptyPrompt = ({ indexName, onBackClick }: APIIndexEmptyPro selectedLanguage={selectedLanguage.id} /> + + + = - {indexData.count > 0 && ( - - - } - footer={ - - - - - - - , - deletedCount: ( - - ), - }} - /> - - - - } - > - - - - {numeral( - indexData.stats?.primaries?.store?.total_data_set_size_in_bytes ?? 0 - ).format('0 b')} - - - - -

- -

- - - - - {numeral( - indexData.stats?.total?.store?.total_data_set_size_in_bytes ?? 0 - ).format('0 b')} - - - - -

- -

-
-
- - - - )} {indexData.count === 0 && ( <> diff --git a/x-pack/plugins/serverless_search/public/application/components/languages/curl.ts b/x-pack/plugins/serverless_search/public/application/components/languages/curl.ts index c679f8d132ddd..32c874ef32eb7 100644 --- a/x-pack/plugins/serverless_search/public/application/components/languages/curl.ts +++ b/x-pack/plugins/serverless_search/public/application/components/languages/curl.ts @@ -32,7 +32,9 @@ export API_KEY="${apiKey}"`, }, iconType: 'curl.svg', id: Languages.CURL, - ingestData: `curl -X POST "\$\{ES_URL\}/_bulk?pretty" \\ + ingestData: ({ ingestPipeline }) => `curl -X POST "\$\{ES_URL\}/_bulk?pretty"${ + ingestPipeline ? `&pipeline=${ingestPipeline}` : '' + }" \\ -H "Authorization: ApiKey "\$\{API_KEY\}"" \\ -H "Content-Type: application/json" \\ -d' @@ -49,7 +51,13 @@ export API_KEY="${apiKey}"`, { "index" : { "_index" : "books" } } {"name": "The Handmaid'"'"'s Tale", "author": "Margaret Atwood", "release_date": "1985-06-01", "page_count": 311} '`, - ingestDataIndex: ({ apiKey, url, indexName }) => `curl -X POST ${url}/_bulk?pretty \\ + ingestDataIndex: ({ + apiKey, + indexName, + ingestPipeline, + }) => `curl -X POST "\$\{url\}/_bulk?pretty${ + ingestPipeline ? `&pipeline=${ingestPipeline}` : '' + }" \\ -H "Authorization: ApiKey ${apiKey}" \\ -H "Content-Type: application/json" \\ -d' diff --git a/x-pack/plugins/serverless_search/public/application/components/languages/dotnet.ts b/x-pack/plugins/serverless_search/public/application/components/languages/dotnet.ts index 1ac3641f67fe9..0d191f2b1e5b6 100644 --- a/x-pack/plugins/serverless_search/public/application/components/languages/dotnet.ts +++ b/x-pack/plugins/serverless_search/public/application/components/languages/dotnet.ts @@ -27,7 +27,7 @@ using Elastic.Clients.Elasticsearch.Serverless.QueryDsl; var client = new ElasticsearchClient("${cloudId}", new ApiKey("${apiKey}"));`, testConnection: `var info = await client.InfoAsync();`, - ingestData: `var doc = new Book + ingestData: ({ ingestPipeline }) => `var doc = new Book { Id = "9780553351927", Name = "Snow Crash", @@ -36,8 +36,10 @@ var client = new ElasticsearchClient("${cloudId}", new ApiKey("${apiKey}"));`, PageCount = 470 }; -var response = await client.IndexAsync(doc, "books");`, - ingestDataIndex: ({ apiKey, cloudId, indexName }) => `using System; +var response = await client.IndexAsync(doc, index: "books"${ + ingestPipeline ? `, x => x.Pipeline("${ingestPipeline}")` : '' + }));`, + ingestDataIndex: ({ apiKey, cloudId, indexName, ingestPipeline }) => `using System; using Elastic.Clients.Elasticsearch.Serverless; using Elastic.Clients.Elasticsearch.Serverless.QueryDsl; @@ -52,7 +54,9 @@ var doc = new Book PageCount = 470 }; -var response = await client.IndexAsync(doc, "${indexName}");`, +var response = await client.IndexAsync(doc, index: "${indexName}"${ + ingestPipeline ? `, x => x.Pipeline("${ingestPipeline}")` : '' + }));`, buildSearchQuery: `var response = await client.SearchAsync(s => s .Index("books") .From(0) diff --git a/x-pack/plugins/serverless_search/public/application/components/languages/go.ts b/x-pack/plugins/serverless_search/public/application/components/languages/go.ts index d92195ad6d82e..ea328f0c9ff6c 100644 --- a/x-pack/plugins/serverless_search/public/application/components/languages/go.ts +++ b/x-pack/plugins/serverless_search/public/application/components/languages/go.ts @@ -46,8 +46,8 @@ func main() { }, iconType: 'go.svg', id: Languages.GO, - ingestData: `ingestResult, err := es.Bulk(). - Index("books"). + ingestData: ({ ingestPipeline }) => `ingestResult, err := es.Bulk(). + Index("books").${ingestPipeline ? `\n Pipeline("${ingestPipeline}").` : ''} Raw(strings.NewReader(\` {"index":{"_id":"9780553351927"}} {"name":"Snow Crash","author":"Neal Stephenson","release_date":"1992-06-01","page_count": 470} @@ -64,7 +64,7 @@ func main() { Do(context.Background()) fmt.Println(ingestResult, err)`, - ingestDataIndex: ({ apiKey, url, indexName }) => `import ( + ingestDataIndex: ({ apiKey, url, indexName, ingestPipeline }) => `import ( "context" "fmt" "log" @@ -83,7 +83,7 @@ func main() { log.Fatalf("Error creating the client: %s", err) } res, err := es.Bulk(). - Index("${indexName}"). + Index("${indexName}").${ingestPipeline ? `\n Pipeline("${ingestPipeline}").` : ''} Raw(strings.NewReader(\` { "index": { "_id": "1"}} {"name": "foo", "title": "bar"}\n\`)). diff --git a/x-pack/plugins/serverless_search/public/application/components/languages/java.ts b/x-pack/plugins/serverless_search/public/application/components/languages/java.ts index c247eb978c15b..4d83be5bd4fca 100644 --- a/x-pack/plugins/serverless_search/public/application/components/languages/java.ts +++ b/x-pack/plugins/serverless_search/public/application/components/languages/java.ts @@ -44,7 +44,7 @@ ElasticsearchClient esClient = new ElasticsearchClient(transport);`, testConnection: `InfoResponse info = esClient.info(); logger.info(info.toString());`, - ingestData: `List books = new ArrayList<>(); + ingestData: ({ ingestPipeline }) => `List books = new ArrayList<>(); books.add(new Book("9780553351927", "Snow Crash", "Neal Stephenson", "1992-06-01", 470)); books.add(new Book("9780441017225", "Revelation Space", "Alastair Reynolds", "2000-03-15", 585)); books.add(new Book("9780451524935", "1984", "George Orwell", "1985-06-01", 328)); @@ -57,7 +57,7 @@ BulkRequest.Builder br = new BulkRequest.Builder(); for (Book book : books) { br.operations(op -> op .index(idx -> idx - .index("books") + .index("books")${ingestPipeline ? `\n .pipeline("${ingestPipeline}")` : ''} .id(product.getId()) .document(book) ) @@ -75,7 +75,7 @@ if (result.errors()) { } } }`, - ingestDataIndex: ({ apiKey, indexName, url }) => `// URL and API key + ingestDataIndex: ({ apiKey, indexName, url, ingestPipeline }) => `// URL and API key String serverUrl = "${url}"; String apiKey = "${apiKey}"; @@ -107,7 +107,9 @@ BulkRequest.Builder br = new BulkRequest.Builder(); for (Book book : books) { br.operations(op -> op .index(idx -> idx - .index("${indexName}") + .index("${indexName}")${ + ingestPipeline ? `\n .pipeline("${ingestPipeline}")` : '' + } .id(product.getId()) .document(book) ) diff --git a/x-pack/plugins/serverless_search/public/application/components/languages/javascript.ts b/x-pack/plugins/serverless_search/public/application/components/languages/javascript.ts index 72d865f1030bc..aefd1c71f467d 100644 --- a/x-pack/plugins/serverless_search/public/application/components/languages/javascript.ts +++ b/x-pack/plugins/serverless_search/public/application/components/languages/javascript.ts @@ -39,7 +39,7 @@ const client = new Client({ }, iconType: 'javascript.svg', id: Languages.JAVASCRIPT, - ingestData: `// Sample books data + ingestData: ({ ingestPipeline }) => `// Sample books data const dataset = [ {"name": "Snow Crash", "author": "Neal Stephenson", "release_date": "1992-06-01", "page_count": 470}, {"name": "Revelation Space", "author": "Alastair Reynolds", "release_date": "2000-03-15", "page_count": 585}, @@ -51,7 +51,7 @@ const dataset = [ // Index with the bulk helper const result = await client.helpers.bulk({ - datasource: dataset, + datasource: dataset,${ingestPipeline ? `\n pipeline: "${ingestPipeline}",` : ''} onDocument (doc) { return { index: { _index: 'my-index-name' }}; } @@ -74,6 +74,7 @@ console.log(result); apiKey, url, indexName, + ingestPipeline, }) => `const { Client } = require('@elastic/elasticsearch-serverless'); const client = new Client({ node: '${url}', @@ -87,7 +88,7 @@ const dataset = [ // Index with the bulk helper const result = await client.helpers.bulk({ - datasource: dataset, + datasource: dataset,${ingestPipeline ? `\n pipeline: "${ingestPipeline}",` : ''} onDocument (doc) { return { index: { _index: '${indexName ?? 'index_name'}' }}; } diff --git a/x-pack/plugins/serverless_search/public/application/components/languages/php.ts b/x-pack/plugins/serverless_search/public/application/components/languages/php.ts index 0a7dbe5758dd9..c156165683a6a 100644 --- a/x-pack/plugins/serverless_search/public/application/components/languages/php.ts +++ b/x-pack/plugins/serverless_search/public/application/components/languages/php.ts @@ -29,7 +29,10 @@ export const phpDefinition: LanguageDefinition = { }, iconType: 'php.svg', id: Languages.PHP, - ingestData: `$body = [ + ingestData: ({ ingestPipeline }) => `$params =[${ + ingestPipeline ? `\n 'pipeline' => '${ingestPipeline}',` : '' + } + 'body' => [ [ "index" => [ "_index" => "books" ]], [ "name" => "Snow Crash", "author" => "Neal Stephenson", "release_date" => "1992-06-01", "page_count" => 470], [ "index" => [ "_index" => "books" ]], @@ -42,22 +45,27 @@ export const phpDefinition: LanguageDefinition = { [ "name" => "Brave New World", "author" => "Aldous Huxley", "release_date" => "1932-06-01", "page_count" => 268], [ "index" => [ "_index" => "books" ]], [ "name" => "The Handmaid's Tale", "author" => "Margaret Atwood", "release_date" => "1985-06-01", "page_count" => 311] -]; +]]; -$response = $client->bulk(body: $body); +$response = $client->bulk($params); echo $response->getStatusCode(); echo (string) $response->getBody();`, - ingestDataIndex: ({ apiKey, url, indexName }) => `$client = ClientBuilder::create() + ingestDataIndex: ({ + apiKey, + url, + indexName, + ingestPipeline, + }) => `$client = ClientBuilder::create() ->setEndpoint('${url}') ->setApiKey('${apiKey}') ->build(); +$params =[${ingestPipeline ? `\n 'pipeline' => '${ingestPipeline}',` : ''} + 'body' => [ + [ 'index' => [ '_index' => '${indexName ?? INDEX_NAME_PLACEHOLDER}', '_id' => '1' ]], + [ 'name' => 'foo', 'title' => 'bar' ] +]]; -$body = [ - [ 'index' => [ '_index' => '${indexName ?? INDEX_NAME_PLACEHOLDER}', '_id' => '1' ]], - [ 'name' => 'foo', 'title' => 'bar' ] -]; - -$response = $client->bulk(body: $body); +$response = $client->bulk($params); echo $response->getStatusCode(); echo (string) $response->getBody(); `, diff --git a/x-pack/plugins/serverless_search/public/application/components/languages/python.ts b/x-pack/plugins/serverless_search/public/application/components/languages/python.ts index dbed5f124e617..266a28b12c181 100644 --- a/x-pack/plugins/serverless_search/public/application/components/languages/python.ts +++ b/x-pack/plugins/serverless_search/public/application/components/languages/python.ts @@ -29,7 +29,7 @@ client = Elasticsearch( }, iconType: 'python.svg', id: Languages.PYTHON, - ingestData: `documents = [ + ingestData: ({ ingestPipeline }) => `documents = [ { "index": { "_index": "books", "_id": "9780553351927"}}, {"name": "Snow Crash", "author": "Neal Stephenson", "release_date": "1992-06-01", "page_count": 470}, { "index": { "_index": "books", "_id": "9780441017225"}}, @@ -44,11 +44,12 @@ client = Elasticsearch( {"name": "The Handmaid's Tale", "author": "Margaret Atwood", "release_date": "1985-06-01", "page_count": 311}, ] -client.bulk(operations=documents)`, +client.bulk(operations=documents${ingestPipeline ? `, pipeline="${ingestPipeline}"` : ''})`, ingestDataIndex: ({ apiKey, url, indexName, + ingestPipeline, }) => `from elasticsearch_serverless import Elasticsearch client = Elasticsearch( @@ -61,7 +62,7 @@ documents = [ {"name": "foo", "title": "bar"}, ] -client.bulk(operations=documents) +client.bulk(operations=documents${ingestPipeline ? `, pipeline="${ingestPipeline}"` : ''}) `, installClient: `python -m pip install elasticsearch-serverless diff --git a/x-pack/plugins/serverless_search/public/application/components/languages/ruby.ts b/x-pack/plugins/serverless_search/public/application/components/languages/ruby.ts index f0553b5d7ec76..19b926878024d 100644 --- a/x-pack/plugins/serverless_search/public/application/components/languages/ruby.ts +++ b/x-pack/plugins/serverless_search/public/application/components/languages/ruby.ts @@ -28,7 +28,7 @@ export const rubyDefinition: LanguageDefinition = { }, iconType: 'ruby.svg', id: Languages.RUBY, - ingestData: `documents = [ + ingestData: ({ ingestPipeline }) => `documents = [ { index: { _index: 'books', data: {name: "Snow Crash", "author": "Neal Stephenson", "release_date": "1992-06-01", "page_count": 470} } }, { index: { _index: 'books', data: {name: "Revelation Space", "author": "Alastair Reynolds", "release_date": "2000-03-15", "page_count": 585} } }, { index: { _index: 'books', data: {name: "1984", "author": "George Orwell", "release_date": "1985-06-01", "page_count": 328} } }, @@ -36,8 +36,13 @@ export const rubyDefinition: LanguageDefinition = { { index: { _index: 'books', data: {name: "Brave New World", "author": "Aldous Huxley", "release_date": "1932-06-01", "page_count": 268} } }, { index: { _index: 'books', data: {name: "The Handmaid's Tale", "author": "Margaret Atwood", "release_date": "1985-06-01", "page_count": 311} } } ] -client.bulk(body: documents)`, - ingestDataIndex: ({ apiKey, url, indexName }) => `client = ElasticsearchServerless::Client.new( +client.bulk(body: documents${ingestPipeline ? `, pipeline: "${ingestPipeline}"` : ''})`, + ingestDataIndex: ({ + apiKey, + url, + indexName, + ingestPipeline, + }) => `client = ElasticsearchServerless::Client.new( api_key: '${apiKey}', url: '${url}' ) @@ -47,7 +52,7 @@ documents = [ indexName ?? INDEX_NAME_PLACEHOLDER }', data: {name: "foo", "title": "bar"} } }, ] -client.bulk(body: documents) +client.bulk(body: documents${ingestPipeline ? `, pipeline: "${ingestPipeline}"` : ''}) `, installClient: `# Requires Ruby version 3.0 or higher diff --git a/x-pack/plugins/serverless_search/public/application/components/overview.tsx b/x-pack/plugins/serverless_search/public/application/components/overview.tsx index 318bcc0b13125..2a5beea0ef2e3 100644 --- a/x-pack/plugins/serverless_search/public/application/components/overview.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/overview.tsx @@ -44,6 +44,7 @@ import { API_KEY_PLACEHOLDER, CLOUD_ID_PLACEHOLDER, ELASTICSEARCH_URL_PLACEHOLDER, + DEFAULT_INGESTION_PIPELINE, } from '../constants'; import { javaDefinition } from './languages/java'; import { languageDefinitions } from './languages/languages'; @@ -55,6 +56,7 @@ import { PipelineOverviewButton } from './pipeline_overview_button'; import { SelectClientCallouts } from './select_client_callouts'; import { PipelineManageButton } from './pipeline_manage_button'; import { OPTIONAL_LABEL } from '../../../common/i18n_string'; +import { useIngestPipelines } from '../hooks/api/use_ingest_pipelines'; export const ElasticsearchOverview = () => { const [selectedLanguage, setSelectedLanguage] = useState(javaDefinition); @@ -82,13 +84,17 @@ export const ElasticsearchOverview = () => { () => (consolePlugin?.EmbeddableConsole ? : null), [consolePlugin] ); + const [selectedPipeline, setSelectedPipeline] = React.useState(''); const codeSnippetArguments: LanguageDefinitionSnippetArguments = { url: elasticsearchURL, apiKey: clientApiKey, cloudId, + ingestPipeline: selectedPipeline, }; + const { data: pipelineData } = useIngestPipelines(); + return ( @@ -302,7 +308,8 @@ export const ElasticsearchOverview = () => { 'ingestData', codeSnippetArguments )} - consoleRequest={getConsoleRequest('ingestData')} + ingestPipelineData={pipelineData?.pipelines} + consoleRequest={getConsoleRequest('ingestData', codeSnippetArguments)} languages={languageDefinitions} selectedLanguage={selectedLanguage} setSelectedLanguage={setSelectedLanguage} @@ -312,6 +319,9 @@ export const ElasticsearchOverview = () => { consolePlugin={consolePlugin} sharePlugin={share} additionalIngestionPanel={} + selectedPipeline={selectedPipeline} + setSelectedPipeline={setSelectedPipeline} + defaultIngestPipeline={DEFAULT_INGESTION_PIPELINE} /> { + const { http } = useKibanaServices(); + return useQuery({ + queryKey: ['fetchIngestPipelines'], + queryFn: async () => + http.fetch>( + `/internal/serverless_search/ingest_pipelines/` + ), + }); +}; diff --git a/x-pack/plugins/serverless_search/server/lib/indices/fetch_index.test.ts b/x-pack/plugins/serverless_search/server/lib/indices/fetch_index.test.ts index 75bc95365bb04..1197c48661b35 100644 --- a/x-pack/plugins/serverless_search/server/lib/indices/fetch_index.test.ts +++ b/x-pack/plugins/serverless_search/server/lib/indices/fetch_index.test.ts @@ -9,7 +9,6 @@ jest.mock('@kbn/search-connectors', () => ({ fetchConnectorByIndexName: jest.fn(), })); -import { ByteSizeValue } from '@kbn/config-schema'; import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import { fetchConnectorByIndexName } from '@kbn/search-connectors'; @@ -19,7 +18,6 @@ describe('fetch index lib function', () => { const mockClient = { indices: { get: jest.fn(), - stats: jest.fn(), }, count: jest.fn(), }; @@ -31,25 +29,7 @@ describe('fetch index lib function', () => { aliases: {}, }, }; - const regularIndexStatsResponse = { - indices: { - 'search-regular-index': { - health: 'green', - size: new ByteSizeValue(108000).toString(), - status: 'open', - total: { - docs: { - count: 100, - deleted: 0, - }, - store: { - size_in_bytes: 108000, - }, - }, - uuid: '83a81e7e-5955-4255-b008-5d6961203f57', - }, - }, - }; + const indexCountResponse = { count: 100, }; @@ -63,7 +43,6 @@ describe('fetch index lib function', () => { it('should return index if all client calls succeed', async () => { mockClient.indices.get.mockResolvedValue({ ...regularIndexResponse }); - mockClient.indices.stats.mockResolvedValue(regularIndexStatsResponse); mockClient.count.mockResolvedValue(indexCountResponse); (fetchConnectorByIndexName as unknown as jest.Mock).mockResolvedValue(indexConnector); @@ -72,44 +51,23 @@ describe('fetch index lib function', () => { aliases: {}, count: 100, connector: indexConnector, - stats: regularIndexStatsResponse.indices[indexName], }, }); }); - it('should throw an error if get index rejects', async () => { const expectedError = new Error('Boom!'); mockClient.indices.get.mockRejectedValue(expectedError); - mockClient.indices.stats.mockResolvedValue(regularIndexStatsResponse); mockClient.count.mockResolvedValue(indexCountResponse); (fetchConnectorByIndexName as unknown as jest.Mock).mockResolvedValue(indexConnector); await expect(fetchIndex(client(), indexName)).rejects.toEqual(expectedError); }); - it('should return partial data if index stats rejects', async () => { - const expectedError = new Error('Boom!'); - - mockClient.indices.get.mockResolvedValue({ ...regularIndexResponse }); - mockClient.indices.stats.mockRejectedValue(expectedError); - mockClient.count.mockResolvedValue(indexCountResponse); - (fetchConnectorByIndexName as unknown as jest.Mock).mockResolvedValue(indexConnector); - - await expect(fetchIndex(client(), indexName)).resolves.toMatchObject({ - index: { - aliases: {}, - count: 100, - connector: indexConnector, - }, - }); - }); - it('should return partial data if index count rejects', async () => { const expectedError = new Error('Boom!'); mockClient.indices.get.mockResolvedValue({ ...regularIndexResponse }); - mockClient.indices.stats.mockResolvedValue(regularIndexStatsResponse); mockClient.count.mockRejectedValue(expectedError); (fetchConnectorByIndexName as unknown as jest.Mock).mockResolvedValue(indexConnector); @@ -118,7 +76,6 @@ describe('fetch index lib function', () => { aliases: {}, count: 0, connector: indexConnector, - stats: regularIndexStatsResponse.indices[indexName], }, }); }); @@ -127,7 +84,6 @@ describe('fetch index lib function', () => { const expectedError = new Error('Boom!'); mockClient.indices.get.mockResolvedValue({ ...regularIndexResponse }); - mockClient.indices.stats.mockResolvedValue(regularIndexStatsResponse); mockClient.count.mockResolvedValue(indexCountResponse); (fetchConnectorByIndexName as unknown as jest.Mock).mockRejectedValue(expectedError); @@ -135,7 +91,6 @@ describe('fetch index lib function', () => { index: { aliases: {}, count: 100, - stats: regularIndexStatsResponse.indices[indexName], }, }); }); diff --git a/x-pack/plugins/serverless_search/server/lib/indices/fetch_index.ts b/x-pack/plugins/serverless_search/server/lib/indices/fetch_index.ts index 000eb1c13ea0a..fec67fd8584c1 100644 --- a/x-pack/plugins/serverless_search/server/lib/indices/fetch_index.ts +++ b/x-pack/plugins/serverless_search/server/lib/indices/fetch_index.ts @@ -4,23 +4,20 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import { fetchConnectorByIndexName } from '@kbn/search-connectors'; - import { FetchIndexResult } from '../../../common/types'; export async function fetchIndex( client: ElasticsearchClient, indexName: string ): Promise { - const [indexDataResult, indexStatsResult, indexCountResult, connectorResult] = - await Promise.allSettled([ - client.indices.get({ index: indexName }), - client.indices.stats({ index: indexName }), - client.count({ index: indexName }), - fetchConnectorByIndexName(client, indexName), - ]); + const [indexDataResult, indexCountResult, connectorResult] = await Promise.allSettled([ + client.indices.get({ index: indexName }), + client.count({ index: indexName }), + fetchConnectorByIndexName(client, indexName), + ]); + if (indexDataResult.status === 'rejected') { throw indexDataResult.reason; } @@ -30,16 +27,11 @@ export async function fetchIndex( const index = indexData[indexName]; const count = indexCountResult.status === 'fulfilled' ? indexCountResult.value.count : 0; const connector = connectorResult.status === 'fulfilled' ? connectorResult.value : undefined; - const stats = - indexStatsResult.status === 'fulfilled' - ? indexStatsResult.value.indices?.[indexName] - : undefined; return { index: { ...index, count, connector, - stats, }, }; } diff --git a/x-pack/plugins/serverless_search/server/plugin.ts b/x-pack/plugins/serverless_search/server/plugin.ts index 0601b099d8a0d..8963a3c78f917 100644 --- a/x-pack/plugins/serverless_search/server/plugin.ts +++ b/x-pack/plugins/serverless_search/server/plugin.ts @@ -29,6 +29,7 @@ import type { import { registerConnectorsRoutes } from './routes/connectors_routes'; import { registerTelemetryUsageCollector } from './collectors/connectors/telemetry'; import { registerMappingRoutes } from './routes/mapping_routes'; +import { registerIngestPipelineRoutes } from './routes/ingest_pipeline_routes'; export interface RouteDependencies { http: CoreSetup['http']; @@ -95,6 +96,7 @@ export class ServerlessSearchPlugin registerConnectorsRoutes(dependencies); registerIndicesRoutes(dependencies); registerMappingRoutes(dependencies); + registerIngestPipelineRoutes(dependencies); if (usageCollection) { registerTelemetryUsageCollector(usageCollection, this.logger); diff --git a/x-pack/plugins/serverless_search/server/routes/ingest_pipeline_routes.ts b/x-pack/plugins/serverless_search/server/routes/ingest_pipeline_routes.ts new file mode 100644 index 0000000000000..4a2720c8712d9 --- /dev/null +++ b/x-pack/plugins/serverless_search/server/routes/ingest_pipeline_routes.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { RouteDependencies } from '../plugin'; + +export const registerIngestPipelineRoutes = ({ router }: RouteDependencies) => { + router.get( + { + path: '/internal/serverless_search/ingest_pipelines/', + validate: {}, + }, + async (context, request, response) => { + const { client } = (await context.core).elasticsearch; + const pipelines = await client.asCurrentUser.ingest.getPipeline(); + + return response.ok({ + body: { + pipelines, + }, + headers: { 'content-type': 'application/json' }, + }); + } + ); +}; diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases_webhook/service.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/cases_webhook/service.test.ts index 4598e8471a838..1e7b492ada959 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/cases_webhook/service.test.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/cases_webhook/service.test.ts @@ -13,6 +13,7 @@ import { CasesWebhookMethods, CasesWebhookPublicConfigurationType, ExternalServi import { Logger } from '@kbn/core/server'; import { loggingSystemMock } from '@kbn/core/server/mocks'; import { actionsConfigMock } from '@kbn/actions-plugin/server/actions_config.mock'; +import { getBasicAuthHeader } from '@kbn/actions-plugin/server/lib'; const logger = loggingSystemMock.create().get() as jest.Mocked; jest.mock('@kbn/actions-plugin/server/lib/axios_utils', () => { @@ -124,6 +125,43 @@ describe('Cases webhook service', () => { ) ).not.toThrow(); }); + + test('uses the basic auth header for authentication', () => { + createExternalService( + actionId, + { + config, + secrets: { user: 'username', password: 'password' }, + }, + logger, + configurationUtilities + ); + + expect(axios.create).toHaveBeenCalledWith({ + headers: { + ...getBasicAuthHeader({ username: 'username', password: 'password' }), + 'content-type': 'application/json', + }, + }); + }); + + test('does not add the basic auth header for authentication if hasAuth=false', () => { + createExternalService( + actionId, + { + config: { ...config, hasAuth: false }, + secrets: { user: 'username', password: 'password' }, + }, + logger, + configurationUtilities + ); + + expect(axios.create).toHaveBeenCalledWith({ + headers: { + 'content-type': 'application/json', + }, + }); + }); }); describe('getIncident', () => { diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases_webhook/service.ts b/x-pack/plugins/stack_connectors/server/connector_types/cases_webhook/service.ts index dd0b28d321a60..46407c18bd081 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/cases_webhook/service.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/cases_webhook/service.ts @@ -8,10 +8,10 @@ import axios, { AxiosResponse } from 'axios'; import { Logger } from '@kbn/core/server'; -import { isString } from 'lodash'; import { renderMustacheStringNoEscape } from '@kbn/actions-plugin/server/lib/mustache_renderer'; import { request } from '@kbn/actions-plugin/server/lib/axios_utils'; import { ActionsConfigurationUtilities } from '@kbn/actions-plugin/server/actions_config'; +import { combineHeadersWithBasicAuthHeader } from '@kbn/actions-plugin/server/lib'; import { validateAndNormalizeUrl, validateJson } from './validators'; import { createServiceError, @@ -69,14 +69,18 @@ export const createExternalService = ( } const createIncidentUrl = removeSlash(createIncidentUrlConfig); + const headersWithBasicAuth = hasAuth + ? combineHeadersWithBasicAuthHeader({ + username: user ?? undefined, + password: password ?? undefined, + headers, + }) + : {}; const axiosInstance = axios.create({ - ...(hasAuth && isString(secrets.user) && isString(secrets.password) - ? { auth: { username: secrets.user, password: secrets.password } } - : {}), headers: { ['content-type']: 'application/json', - ...(headers != null ? headers : {}), + ...headersWithBasicAuth, }, }); diff --git a/x-pack/plugins/stack_connectors/server/connector_types/jira/service.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/jira/service.test.ts index f6b4dcf909abe..34e0f1f799ce5 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/jira/service.test.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/jira/service.test.ts @@ -13,6 +13,7 @@ import { ExternalService } from './types'; import { Logger } from '@kbn/core/server'; import { loggingSystemMock } from '@kbn/core/server/mocks'; import { actionsConfigMock } from '@kbn/actions-plugin/server/actions_config.mock'; +import { getBasicAuthHeader } from '@kbn/actions-plugin/server'; const logger = loggingSystemMock.create().get() as jest.Mocked; interface ResponseError extends Error { @@ -204,6 +205,21 @@ describe('Jira service', () => { ) ).toThrow(); }); + + test('uses the basic auth header for authentication', () => { + createExternalService( + { + config: { apiUrl: 'https://coolsite.net/', projectKey: 'CK' }, + secrets: { apiToken: 'token', email: 'elastic@elastic.com' }, + }, + logger, + configurationUtilities + ); + + expect(axios.create).toHaveBeenCalledWith({ + headers: getBasicAuthHeader({ username: 'elastic@elastic.com', password: 'token' }), + }); + }); }); describe('getIncident', () => { diff --git a/x-pack/plugins/stack_connectors/server/connector_types/jira/service.ts b/x-pack/plugins/stack_connectors/server/connector_types/jira/service.ts index f0ad0e3062ccc..3cd5115234da1 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/jira/service.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/jira/service.ts @@ -15,6 +15,7 @@ import { throwIfResponseIsNotValid, } from '@kbn/actions-plugin/server/lib/axios_utils'; import { ActionsConfigurationUtilities } from '@kbn/actions-plugin/server/actions_config'; +import { getBasicAuthHeader } from '@kbn/actions-plugin/server'; import { CreateCommentParams, CreateIncidentParams, @@ -66,7 +67,7 @@ export const createExternalService = ( const searchUrl = `${urlWithoutTrailingSlash}/${BASE_URL}/search`; const axiosInstance = axios.create({ - auth: { username: email, password: apiToken }, + headers: getBasicAuthHeader({ username: email, password: apiToken }), }); const getIncidentViewURL = (key: string) => { diff --git a/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/utils.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/utils.test.ts index f151affe8a597..8bc9fa0565d8f 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/utils.test.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/utils.test.ts @@ -20,6 +20,7 @@ import type { ResponseError } from './types'; import { connectorTokenClientMock } from '@kbn/actions-plugin/server/lib/connector_token_client.mock'; import { actionsConfigMock } from '@kbn/actions-plugin/server/actions_config.mock'; import { getOAuthJwtAccessToken } from '@kbn/actions-plugin/server/lib/get_oauth_jwt_access_token'; +import { getBasicAuthHeader } from '@kbn/actions-plugin/server'; jest.mock('@kbn/actions-plugin/server/lib/get_oauth_jwt_access_token', () => ({ getOAuthJwtAccessToken: jest.fn(), @@ -189,7 +190,7 @@ describe('utils', () => { expect(createAxiosInstanceMock).toHaveBeenCalledTimes(1); expect(createAxiosInstanceMock).toHaveBeenCalledWith({ - auth: { password: 'password', username: 'username' }, + headers: getBasicAuthHeader({ username: 'username', password: 'password' }), }); }); diff --git a/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/utils.ts b/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/utils.ts index 44171d1a11947..8998fa9f94c63 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/utils.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/utils.ts @@ -11,6 +11,7 @@ import { addTimeZoneToDate, getErrorMessage } from '@kbn/actions-plugin/server/l import { ActionsConfigurationUtilities } from '@kbn/actions-plugin/server/actions_config'; import { ConnectorTokenClientContract } from '@kbn/actions-plugin/server/types'; import { getOAuthJwtAccessToken } from '@kbn/actions-plugin/server/lib/get_oauth_jwt_access_token'; +import { getBasicAuthHeader } from '@kbn/actions-plugin/server'; import { ExternalServiceCredentials, Incident, @@ -115,7 +116,7 @@ export const getAxiosInstance = ({ if (!isOAuth && username && password) { axiosInstance = axios.create({ - auth: { username, password }, + headers: getBasicAuthHeader({ username, password }), }); } else { axiosInstance = axios.create(); diff --git a/x-pack/plugins/stack_connectors/server/connector_types/resilient/resilient.ts b/x-pack/plugins/stack_connectors/server/connector_types/resilient/resilient.ts index 4c8175e52ab8f..1351488dbf892 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/resilient/resilient.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/resilient/resilient.ts @@ -7,7 +7,7 @@ import { AxiosError } from 'axios'; import { omitBy, isNil } from 'lodash/fp'; -import { CaseConnector, ServiceParams } from '@kbn/actions-plugin/server'; +import { CaseConnector, getBasicAuthHeader, ServiceParams } from '@kbn/actions-plugin/server'; import { schema, Type } from '@kbn/config-schema'; import { getErrorMessage } from '@kbn/actions-plugin/server/lib/axios_utils'; import { @@ -96,12 +96,10 @@ export class ResilientConnector extends CaseConnector< } private getAuthHeaders() { - const token = Buffer.from( - this.secrets.apiKeyId + ':' + this.secrets.apiKeySecret, - 'utf8' - ).toString('base64'); - - return { Authorization: `Basic ${token}` }; + return getBasicAuthHeader({ + username: this.secrets.apiKeyId, + password: this.secrets.apiKeySecret, + }); } private getOrgUrl() { diff --git a/x-pack/plugins/stack_connectors/server/connector_types/webhook/index.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/webhook/index.test.ts index bd24314fa79b7..9e82b2d2c31e7 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/webhook/index.test.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/webhook/index.test.ts @@ -348,13 +348,10 @@ describe('execute()', () => { delete requestMock.mock.calls[0][0].configurationUtilities; expect(requestMock.mock.calls[0][0]).toMatchInlineSnapshot(` Object { - "auth": Object { - "password": "123", - "username": "abc", - }, "axios": undefined, "data": "some data", "headers": Object { + "Authorization": "Basic YWJjOjEyMw==", "aheader": "a value", }, "logger": Object { diff --git a/x-pack/plugins/stack_connectors/server/connector_types/webhook/index.ts b/x-pack/plugins/stack_connectors/server/connector_types/webhook/index.ts index b71c478177178..a3383ee844016 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/webhook/index.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/webhook/index.ts @@ -25,6 +25,7 @@ import { SecurityConnectorFeatureId, } from '@kbn/actions-plugin/common/types'; import { renderMustacheString } from '@kbn/actions-plugin/server/lib/mustache_renderer'; +import { combineHeadersWithBasicAuthHeader } from '@kbn/actions-plugin/server/lib'; import { SSLCertType, WebhookAuthType } from '../../../common/webhook/constants'; import { getRetryAfterIntervalFromHeaders } from '../lib/http_response_retry_header'; import { nullableType } from '../lib/nullable'; @@ -210,6 +211,7 @@ export async function executor( isString(secrets.password) ? { auth: { username: secrets.user, password: secrets.password } } : {}; + const sslCertificate = authType === WebhookAuthType.SSL && ((isString(secrets.crt) && isString(secrets.key)) || isString(secrets.pfx)) @@ -233,14 +235,19 @@ export async function executor( ...(ca ? { ca: Buffer.from(ca, 'base64') } : {}), }; + const headersWithBasicAuth = combineHeadersWithBasicAuthHeader({ + username: basicAuth.auth?.username, + password: basicAuth.auth?.password, + headers, + }); + const result: Result> = await promiseResult( request({ axios: axiosInstance, method, url, logger, - ...basicAuth, - headers: headers ? headers : {}, + headers: headersWithBasicAuth, data, configurationUtilities, sslOverrides, diff --git a/x-pack/plugins/stack_connectors/server/connector_types/xmatters/index.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/xmatters/index.test.ts index f39f510282984..23f7a01887126 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/xmatters/index.test.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/xmatters/index.test.ts @@ -5,11 +5,7 @@ * 2.0. */ -jest.mock('./post_xmatters', () => ({ - postXmatters: jest.fn(), -})); -import { postXmatters } from './post_xmatters'; - +import axios from 'axios'; import { Logger } from '@kbn/core/server'; import { ConnectorTypeConfigType, @@ -28,8 +24,22 @@ import { import { actionsConfigMock } from '@kbn/actions-plugin/server/actions_config.mock'; import { ActionsConfigurationUtilities } from '@kbn/actions-plugin/server/actions_config'; import { loggerMock } from '@kbn/logging-mocks'; +import * as utils from '@kbn/actions-plugin/server/lib/axios_utils'; + +jest.mock('axios'); +jest.mock('@kbn/actions-plugin/server/lib/axios_utils', () => { + const originalUtils = jest.requireActual('@kbn/actions-plugin/server/lib/axios_utils'); + return { + ...originalUtils, + request: jest.fn(), + patch: jest.fn(), + }; +}); + +axios.create = jest.fn(() => axios); +const requestMock = utils.request as jest.Mock; +axios.create = jest.fn(() => axios); -const postxMattersMock = postXmatters as jest.Mock; const services: Services = actionsMock.createServices(); const mockedLogger: jest.Mocked = loggerMock.create(); @@ -383,8 +393,8 @@ describe('connector validation', () => { describe('execute()', () => { beforeEach(() => { - postxMattersMock.mockReset(); - postxMattersMock.mockResolvedValue({ + requestMock.mockReset(); + requestMock.mockResolvedValue({ status: 200, statusText: '', data: '', @@ -415,14 +425,10 @@ describe('execute()', () => { logger: mockedLogger, }); - expect(postxMattersMock.mock.calls[0][0]).toMatchInlineSnapshot(` + const { method, url, headers, data } = requestMock.mock.calls[0][0]; + + expect({ method, url, headers, data }).toMatchInlineSnapshot(` Object { - "basicAuth": Object { - "auth": Object { - "password": "123", - "username": "abc", - }, - }, "data": Object { "alertActionGroupName": "Small t-shirt", "date": "2022-01-18T19:01:08.818Z", @@ -432,6 +438,10 @@ describe('execute()', () => { "spaceId": "default", "tags": "test1, test2", }, + "headers": Object { + "Authorization": "Basic YWJjOjEyMw==", + }, + "method": "post", "url": "https://abc.def/my-xmatters", } `); @@ -442,7 +452,7 @@ describe('execute()', () => { configUrl: 'https://abc.def/my-xmatters', usesBasic: true, }; - postxMattersMock.mockRejectedValueOnce({ + requestMock.mockRejectedValueOnce({ tag: 'err', message: 'maxContentLength size of 1000000 exceeded', }); @@ -496,9 +506,10 @@ describe('execute()', () => { logger: mockedLogger, }); - expect(postxMattersMock.mock.calls[0][0]).toMatchInlineSnapshot(` + const { method, url, headers, data } = requestMock.mock.calls[0][0]; + + expect({ method, url, headers, data }).toMatchInlineSnapshot(` Object { - "basicAuth": undefined, "data": Object { "alertActionGroupName": "Small t-shirt", "date": "2022-01-18T19:01:08.818Z", @@ -508,6 +519,8 @@ describe('execute()', () => { "spaceId": "default", "tags": "test1, test2", }, + "headers": undefined, + "method": "post", "url": "https://abc.def/my-xmatters?apiKey=someKey", } `); diff --git a/x-pack/plugins/stack_connectors/server/connector_types/xmatters/post_xmatters.ts b/x-pack/plugins/stack_connectors/server/connector_types/xmatters/post_xmatters.ts index 1fcec5a4238f7..2c2a08901cec3 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/xmatters/post_xmatters.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/xmatters/post_xmatters.ts @@ -9,6 +9,7 @@ import axios, { AxiosResponse } from 'axios'; import { Logger } from '@kbn/core/server'; import { ActionsConfigurationUtilities } from '@kbn/actions-plugin/server/actions_config'; import { request } from '@kbn/actions-plugin/server/lib/axios_utils'; +import { combineHeadersWithBasicAuthHeader } from '@kbn/actions-plugin/server/lib'; interface PostXmattersOptions { url: string; @@ -42,7 +43,10 @@ export async function postXmatters( method: 'post', url, logger, - ...basicAuth, + headers: combineHeadersWithBasicAuthHeader({ + username: basicAuth?.auth.username, + password: basicAuth?.auth.password, + }), data, configurationUtilities, validateStatus: () => true, diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 526bee1ed8169..04b9e05b48957 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -686,7 +686,6 @@ "core.deprecations.elasticsearchUsername.manualSteps2": "Ajoutez le paramètre \"elasticsearch.serviceAccountToken\" à kibana.yml.", "core.deprecations.elasticsearchUsername.manualSteps3": "Supprimez \"elasticsearch.username\" et \"elasticsearch.password\" de kibana.yml.", "core.deprecations.noCorrectiveAction": "Ce déclassement ne peut pas être résolu automatiquement.", - "core.euiAbsoluteTab.dateFormatHint": "Appuyez sur la touche Entrée pour analyser l'élément en tant que date.", "core.euiAccordionChildrenLoading.message": "Chargement", "core.euiAutoRefresh.autoRefreshLabel": "Actualisation automatique", "core.euiAutoRefresh.buttonLabelOff": "L'actualisation automatique est désactivée", @@ -2278,7 +2277,7 @@ "discover.showingDefaultDataViewWarningDescription": "Affichage de la vue de données par défaut : \"{loadedDataViewTitle}\" ({loadedDataViewId})", "discover.showingSavedDataViewWarningDescription": "Affichage de la vue de données enregistrée : \"{ownDataViewTitle}\" ({ownDataViewId})", "discover.singleDocRoute.errorMessage": "Aucune donnée correspondante pour l'ID {dataViewId}", - "discover.textBasedMode.selectedColumnsCallout": "Affichage de {selectedColumnsNumber} champs sur {textBasedQueryColumnsNumber}. Ajoutez-en d’autres depuis la liste des champs disponibles.", + "discover.esqlMode.selectedColumnsCallout": "Affichage de {selectedColumnsNumber} champs sur {esqlQueryColumnsNumber}. Ajoutez-en d’autres depuis la liste des champs disponibles.", "discover.valueIsNotConfiguredDataViewIDWarningTitle": "{stateVal} n'est pas un ID de vue de données configuré", "discover.viewAlert.dataViewErrorText": "Échec de la vue des données de la règle d'alerte avec l'ID {alertId}.", "discover.advancedSettings.context.defaultSizeText": "Le nombre d'entrées connexes à afficher dans la vue contextuelle", @@ -2414,10 +2413,10 @@ "discover.grid.flyout.toastColumnRemoved": "La colonne \"{columnName}\" a été supprimée.", "discover.grid.tableRow.actionsLabel": "Actions", "discover.grid.tableRow.docViewerDetailHeading": "Document", - "discover.grid.tableRow.docViewerTextBasedDetailHeading": "Ligne", + "discover.grid.tableRow.docViewerEsqlDetailHeading": "Ligne", "discover.grid.tableRow.mobileFlyoutActionsButton": "Actions", "discover.grid.tableRow.moreFlyoutActionsButton": "Plus d'actions", - "discover.grid.tableRow.textBasedDetailHeading": "Ligne développée", + "discover.grid.tableRow.esqlDetailHeading": "Ligne développée", "discover.grid.tableRow.viewSingleDocumentLinkLabel": "Afficher un seul document", "discover.grid.tableRow.viewSurroundingDocumentsHover": "Inspectez des documents qui ont été créés avant et après ce document. Seuls les filtres épinglés restent actifs dans la vue Documents relatifs.", "discover.grid.tableRow.viewSurroundingDocumentsLinkLabel": "Afficher les documents alentour", @@ -13933,7 +13932,6 @@ "xpack.enterpriseSearch.crawler.deleteDomainModal.description": "Supprimer le domaine {domainUrl} de votre robot d'indexation. Cela supprimera également tous les points d'entrée et toutes les règles d'indexation que vous avez configurés. Tous les documents associés à ce domaine seront supprimés lors de la prochaine indexation. {thisCannotBeUndoneMessage}", "xpack.enterpriseSearch.crawler.entryPointsTable.emptyMessageDescription": "{link} pour spécifier un point d'entrée pour le robot d'indexation", "xpack.enterpriseSearch.deleteConnectorModal.li.myconnectornameRelatedIndexLabel": "{connectorName} (Index associé : {deleteModalIndexName})", - "xpack.enterpriseSearch.endpointsHeader.apiKey.activeKeys": "{number} clés d'API actives", "xpack.enterpriseSearch.errorConnectingState.cloudErrorMessage": "Les nœuds Enterprise Search fonctionnent-ils dans votre déploiement cloud ? {deploymentSettingsLink}", "xpack.enterpriseSearch.errorConnectingState.description1": "Impossible d'établir une connexion à Enterprise Search avec l'URL hôte {enterpriseSearchUrl} en raison de l'erreur suivante :", "xpack.enterpriseSearch.errorConnectingState.description2": "Vérifiez que l'URL hôte est correctement configurée dans {configFile}.", @@ -16150,12 +16148,7 @@ "xpack.enterpriseSearch.overview.gettingStarted.testConnection.description": "Envoyez une requête de test pour confirmer que votre client de langage et votre instance Elasticsearch sont opérationnels.", "xpack.enterpriseSearch.overview.gettingStarted.testConnection.title": "Tester votre connexion", "xpack.enterpriseSearch.overview.navTitle": "Aperçu", - "xpack.enterpriseSearch.overview.pageTemplate.apiKey.copyApiEndpoint": "Copier le point de terminaison Elasticsearch dans le presse-papiers", "xpack.enterpriseSearch.overview.setupCta.description": "Ajoutez des fonctions de recherche à votre application ou à votre organisation interne avec Elastic App Search et Workplace Search. Regardez la vidéo pour savoir ce qu'il est possible de faire lorsque la recherche est facilitée.", - "xpack.enterpriseSearch.pageTemplate.apiKey.copied": "Copié", - "xpack.enterpriseSearch.pageTemplate.apiKey.elasticsearchEndpoint": "Point de terminaison Elasticsearch :", - "xpack.enterpriseSearch.pageTemplate.apiKey.manageLabel": "Gérer", - "xpack.enterpriseSearch.pageTemplate.apiKey.newButtonLabel": "Nouvelle clé d'API", "xpack.enterpriseSearch.pageTemplate.endpointsButtonLabel": "Points de terminaison et clés d'API", "xpack.enterpriseSearch.passwordLabel": "Mot de passe", "xpack.enterpriseSearch.pipeline.title": "Transformer et enrichir vos données", @@ -38137,7 +38130,6 @@ "xpack.serverlessSearch.indexManagement.indexDetails.overview.aliasesFlyout.title": "{indexName} Alias", "xpack.serverlessSearch.indexManagement.indexDetails.overview.emptyPrompt.api.body": "Personnalisez ces variables en fonction de votre contenu. Pour un guide d'installation complet, consultez notre guide {getStartedLink}.", "xpack.serverlessSearch.indexManagement.indexDetails.overview.emptyPrompt.body": "Alimentez votre index avec des données en utilisant {logstashLink}, {beatsLink}, {connectorsLink} ou RESTful {apiCallsLink}.", - "xpack.serverlessSearch.indexManagement.indexDetails.overview.storagePanel.documentCount": "{documentCount} documents / {deletedCount} supprimés", "xpack.serverlessSearch.searchConnectors.configurationConnector.config.documentation.description": "Ce connecteur prend en charge plusieurs méthodes d'authentification. Demandez à votre administrateur les informations d'identification correctes pour la connexion. {documentationUrl}", "xpack.serverlessSearch.apiKey.apiKeyStepDescription": "Cette clé ne s’affichera qu’une fois, conservez-la donc en lieu sûr. Nous ne conservons pas vos clés d’API, vous devrez donc générer une clé de remplacement si vous la perdez.", "xpack.serverlessSearch.apiKey.apiKeyStepTitle": "Stocker cette clé d'API", @@ -38263,9 +38255,6 @@ "xpack.serverlessSearch.indexManagement.indexDetails.overview.error.description": "Une erreur s'est produite lors du chargement de l'index.", "xpack.serverlessSearch.indexManagement.indexDetails.overview.error.title": "Impossible de charger l'index", "xpack.serverlessSearch.indexManagement.indexDetails.overview.loading.title": "Chargement de l'index", - "xpack.serverlessSearch.indexManagement.indexDetails.overview.storagePanel.primaryLabel": "Principale", - "xpack.serverlessSearch.indexManagement.indexDetails.overview.storagePanel.title": "Stockage", - "xpack.serverlessSearch.indexManagement.indexDetails.overview.storagePanel.totalLabel": "Total", "xpack.serverlessSearch.indexManagementTab.documents": "Documents", "xpack.serverlessSearch.indexManagementTab.documents.noMappings": "Aucun document trouvé pour l'index", "xpack.serverlessSearch.indexMappings.ingestPipelinesDocs.description": "Vous souhaitez ajouter des champs personnalisés ou utiliser des modèles de ML entraînés pour analyser et enrichir vos documents indexés ? Utilisez des pipelines d'ingestion spécifiques de l'index pour personnaliser les documents selon vos besoins.", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index e7c5c729dcd78..c6198a0e10efe 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -686,7 +686,6 @@ "core.deprecations.elasticsearchUsername.manualSteps2": "「elasticsearch.serviceAccountToken」設定をkibana.ymlに追加します。", "core.deprecations.elasticsearchUsername.manualSteps3": "kibana.ymlから「elasticsearch.username」と「elasticsearch.password」を削除します。", "core.deprecations.noCorrectiveAction": "この廃止予定は自動的に解決できません。", - "core.euiAbsoluteTab.dateFormatHint": "日付として解析するには、Enterキーを押してください。", "core.euiAccordionChildrenLoading.message": "読み込み中", "core.euiAutoRefresh.autoRefreshLabel": "自動更新", "core.euiAutoRefresh.buttonLabelOff": "自動更新はオフです", @@ -2276,7 +2275,7 @@ "discover.showingDefaultDataViewWarningDescription": "デフォルトデータビューを表示しています:\"{loadedDataViewTitle}\" ({loadedDataViewId})", "discover.showingSavedDataViewWarningDescription": "保存されたデータビューを表示しています:\"{ownDataViewTitle}\" ({ownDataViewId})", "discover.singleDocRoute.errorMessage": "ID {dataViewId}の一致するデータビューが見つかりません", - "discover.textBasedMode.selectedColumnsCallout": "{textBasedQueryColumnsNumber}フィールド中{selectedColumnsNumber}フィールドを表示中です。利用可能なフィールドリストからさらに追加します。", + "discover.esqlMode.selectedColumnsCallout": "{esqlQueryColumnsNumber}フィールド中{selectedColumnsNumber}フィールドを表示中です。利用可能なフィールドリストからさらに追加します。", "discover.valueIsNotConfiguredDataViewIDWarningTitle": "{stateVal}は設定されたデータビューIDではありません", "discover.viewAlert.dataViewErrorText": "id {alertId}のアラートルールのデータビューのエラー。", "discover.advancedSettings.context.defaultSizeText": "コンテキストビューに表示される周りのエントリーの数", @@ -2412,10 +2411,10 @@ "discover.grid.flyout.toastColumnRemoved": "列'{columnName}'が削除されました", "discover.grid.tableRow.actionsLabel": "アクション", "discover.grid.tableRow.docViewerDetailHeading": "ドキュメント", - "discover.grid.tableRow.docViewerTextBasedDetailHeading": "行", + "discover.grid.tableRow.docViewerEsqlDetailHeading": "行", "discover.grid.tableRow.mobileFlyoutActionsButton": "アクション", "discover.grid.tableRow.moreFlyoutActionsButton": "さらにアクションを表示", - "discover.grid.tableRow.textBasedDetailHeading": "展開された行", + "discover.grid.tableRow.esqlDetailHeading": "展開された行", "discover.grid.tableRow.viewSingleDocumentLinkLabel": "単一のドキュメントを表示", "discover.grid.tableRow.viewSurroundingDocumentsHover": "このドキュメントの前後に出現したドキュメントを検査します。周りのドキュメントビューでは、固定されたフィルターのみがアクティブのままです。", "discover.grid.tableRow.viewSurroundingDocumentsLinkLabel": "周りのドキュメントを表示", @@ -13912,7 +13911,6 @@ "xpack.enterpriseSearch.crawler.deleteDomainModal.description": "ドメイン{domainUrl}をクローラーから削除します。これにより、設定したすべてのエントリポイントとクロールルールも削除されます。このドメインに関連するすべてのドキュメントは、次回のクロールで削除されます。{thisCannotBeUndoneMessage}", "xpack.enterpriseSearch.crawler.entryPointsTable.emptyMessageDescription": "クローラーのエントリポイントを指定するには、{link}してください", "xpack.enterpriseSearch.deleteConnectorModal.li.myconnectornameRelatedIndexLabel": "{connectorName}(関連付けられたインデックス:{deleteModalIndexName})", - "xpack.enterpriseSearch.endpointsHeader.apiKey.activeKeys": "{number}個のアクティブなAPIキー", "xpack.enterpriseSearch.errorConnectingState.cloudErrorMessage": "クラウドデプロイのエンタープライズ サーチノードが実行中ですか?{deploymentSettingsLink}", "xpack.enterpriseSearch.errorConnectingState.description1": "次のエラーのため、ホストURL {enterpriseSearchUrl}では、エンタープライズ サーチへの接続を確立できません。", "xpack.enterpriseSearch.errorConnectingState.description2": "ホストURLが{configFile}で正しく構成されていることを確認してください。", @@ -16128,12 +16126,7 @@ "xpack.enterpriseSearch.overview.gettingStarted.testConnection.description": "テストリクエストを送信して、言語クライアントとElasticsearchインスタンスが起動し、実行中であることを確認してください。", "xpack.enterpriseSearch.overview.gettingStarted.testConnection.title": "接続をテスト", "xpack.enterpriseSearch.overview.navTitle": "概要", - "xpack.enterpriseSearch.overview.pageTemplate.apiKey.copyApiEndpoint": "Elasticsearchエンドポイントをクリップボードにコピーします。", "xpack.enterpriseSearch.overview.setupCta.description": "Elastic App Search および Workplace Search を使用して、アプリまたは社内組織に検索を追加できます。検索が簡単になるとどのような利点があるのかについては、動画をご覧ください。", - "xpack.enterpriseSearch.pageTemplate.apiKey.copied": "コピー完了", - "xpack.enterpriseSearch.pageTemplate.apiKey.elasticsearchEndpoint": "Elasticsearchエンドポイント:", - "xpack.enterpriseSearch.pageTemplate.apiKey.manageLabel": "管理", - "xpack.enterpriseSearch.pageTemplate.apiKey.newButtonLabel": "新しいAPIキー", "xpack.enterpriseSearch.pageTemplate.endpointsButtonLabel": "エンドポイントとAPIキー", "xpack.enterpriseSearch.passwordLabel": "パスワード", "xpack.enterpriseSearch.pipeline.title": "データの変換とエンリッチ", @@ -38105,7 +38098,6 @@ "xpack.serverlessSearch.indexManagement.indexDetails.overview.aliasesFlyout.title": "{indexName}エイリアス", "xpack.serverlessSearch.indexManagement.indexDetails.overview.emptyPrompt.api.body": "これらの変数をコンテンツに合わせてカスタマイズします。詳細なセットアップガイドについては、{getStartedLink}ガイドをご覧ください。", "xpack.serverlessSearch.indexManagement.indexDetails.overview.emptyPrompt.body": "{logstashLink}、{beatsLink}、{connectorsLink}、またはRESTful {apiCallsLink}を使用して、データにインデックスを入力します。", - "xpack.serverlessSearch.indexManagement.indexDetails.overview.storagePanel.documentCount": "{documentCount}件のドキュメント / {deletedCount}削除済み", "xpack.serverlessSearch.searchConnectors.configurationConnector.config.documentation.description": "このコネクターは複数の認証方法をサポートします。正しい接続資格情報については、管理者に確認してください。{documentationUrl}", "xpack.serverlessSearch.apiKey.apiKeyStepDescription": "このキーは一度しか表示されないため、安全な場所に保存しておいてください。当社はお客様のAPIキーを保存しません。キーを紛失した場合は、代替キーを生成する必要があります。", "xpack.serverlessSearch.apiKey.apiKeyStepTitle": "このAPIキーを保存", @@ -38231,9 +38223,6 @@ "xpack.serverlessSearch.indexManagement.indexDetails.overview.error.description": "インデックスの読み込みエラーが発生しました。", "xpack.serverlessSearch.indexManagement.indexDetails.overview.error.title": "インデックスを読み込めません", "xpack.serverlessSearch.indexManagement.indexDetails.overview.loading.title": "インデックスを読み込んでいます", - "xpack.serverlessSearch.indexManagement.indexDetails.overview.storagePanel.primaryLabel": "プライマリ", - "xpack.serverlessSearch.indexManagement.indexDetails.overview.storagePanel.title": "ストレージ", - "xpack.serverlessSearch.indexManagement.indexDetails.overview.storagePanel.totalLabel": "合計", "xpack.serverlessSearch.indexManagementTab.documents": "ドキュメント", "xpack.serverlessSearch.indexManagementTab.documents.noMappings": "インデックスドキュメントが見つかりません", "xpack.serverlessSearch.indexMappings.ingestPipelinesDocs.description": "カスタムフィールドを追加したり、学習済みのMLモデルを使用してインデックスされたドキュメントを分析したり、インデックスされたドキュメントをリッチ化したいですか?インデックス固有のインジェストパイプラインを使用して、ニーズに合わせてドキュメントをカスタマイズします。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index b33c76617db9e..3d5c64342c7a8 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -688,7 +688,6 @@ "core.deprecations.elasticsearchUsername.manualSteps2": "将“elasticsearch.serviceAccountToken”设置添加到 kibana.yml。", "core.deprecations.elasticsearchUsername.manualSteps3": "从 kibana.yml 中移除“elasticsearch.username”和“elasticsearch.password”。", "core.deprecations.noCorrectiveAction": "无法自动解决此弃用。", - "core.euiAbsoluteTab.dateFormatHint": "按 Enter 键解析为日期。", "core.euiAccordionChildrenLoading.message": "正在加载", "core.euiAutoRefresh.autoRefreshLabel": "自动刷新", "core.euiAutoRefresh.buttonLabelOff": "自动刷新已关闭", @@ -2280,7 +2279,7 @@ "discover.showingDefaultDataViewWarningDescription": "正在显示默认数据视图:“{loadedDataViewTitle}”({loadedDataViewId})", "discover.showingSavedDataViewWarningDescription": "正在显示已保存数据视图:“{ownDataViewTitle}”({ownDataViewId})", "discover.singleDocRoute.errorMessage": "没有与 ID {dataViewId} 相匹配的数据视图", - "discover.textBasedMode.selectedColumnsCallout": "正在显示 {selectedColumnsNumber} 个字段,共 {textBasedQueryColumnsNumber} 个。从可用字段列表中添加更多字段。", + "discover.esqlMode.selectedColumnsCallout": "正在显示 {selectedColumnsNumber} 个字段,共 {esqlQueryColumnsNumber} 个。从可用字段列表中添加更多字段。", "discover.valueIsNotConfiguredDataViewIDWarningTitle": "{stateVal} 不是配置的数据视图 ID", "discover.viewAlert.dataViewErrorText": "ID 为 {alertId} 的告警规则的数据视图失败。", "discover.advancedSettings.context.defaultSizeText": "要在上下文视图中显示的周围条目数目", @@ -2416,10 +2415,10 @@ "discover.grid.flyout.toastColumnRemoved": "已移除列“{columnName}”", "discover.grid.tableRow.actionsLabel": "操作", "discover.grid.tableRow.docViewerDetailHeading": "文档", - "discover.grid.tableRow.docViewerTextBasedDetailHeading": "行", + "discover.grid.tableRow.docViewerEsqlDetailHeading": "行", "discover.grid.tableRow.mobileFlyoutActionsButton": "操作", "discover.grid.tableRow.moreFlyoutActionsButton": "更多操作", - "discover.grid.tableRow.textBasedDetailHeading": "已展开行", + "discover.grid.tableRow.esqlDetailHeading": "已展开行", "discover.grid.tableRow.viewSingleDocumentLinkLabel": "查看单个文档", "discover.grid.tableRow.viewSurroundingDocumentsHover": "检查在此文档之前和之后出现的文档。在周围文档视图中,仅已固定筛选仍处于活动状态。", "discover.grid.tableRow.viewSurroundingDocumentsLinkLabel": "查看周围文档", @@ -13938,7 +13937,6 @@ "xpack.enterpriseSearch.crawler.deleteDomainModal.description": "从网络爬虫中移除域 {domainUrl}。这还会删除您已设置的所有入口点和爬网规则。将在下次爬网时移除与此域相关的任何文档。{thisCannotBeUndoneMessage}", "xpack.enterpriseSearch.crawler.entryPointsTable.emptyMessageDescription": "{link}以指定网络爬虫的入口点", "xpack.enterpriseSearch.deleteConnectorModal.li.myconnectornameRelatedIndexLabel": "{connectorName}(相关索引:{deleteModalIndexName})", - "xpack.enterpriseSearch.endpointsHeader.apiKey.activeKeys": "{number} 个活动 API 密钥", "xpack.enterpriseSearch.errorConnectingState.cloudErrorMessage": "您的云部署是否正在运行 Enterprise Search 节点?{deploymentSettingsLink}", "xpack.enterpriseSearch.errorConnectingState.description1": "由于以下错误,我们无法与主机 URL {enterpriseSearchUrl} 的 Enterprise Search 建立连接:", "xpack.enterpriseSearch.errorConnectingState.description2": "确保在 {configFile} 中已正确配置主机 URL。", @@ -16155,12 +16153,7 @@ "xpack.enterpriseSearch.overview.gettingStarted.testConnection.description": "发送测试请求,以确认您的语言客户端和 Elasticsearch 实例已启动并正在运行。", "xpack.enterpriseSearch.overview.gettingStarted.testConnection.title": "测试您的连接", "xpack.enterpriseSearch.overview.navTitle": "概览", - "xpack.enterpriseSearch.overview.pageTemplate.apiKey.copyApiEndpoint": "复制 Elasticsearch 终端到剪贴板。", "xpack.enterpriseSearch.overview.setupCta.description": "通过 Elastic App Search 和 Workplace Search,将搜索添加到您的应用或内部组织中。观看视频,了解方便易用的搜索功能可以帮您做些什么。", - "xpack.enterpriseSearch.pageTemplate.apiKey.copied": "已复制", - "xpack.enterpriseSearch.pageTemplate.apiKey.elasticsearchEndpoint": "Elasticsearch 终端:", - "xpack.enterpriseSearch.pageTemplate.apiKey.manageLabel": "管理", - "xpack.enterpriseSearch.pageTemplate.apiKey.newButtonLabel": "新 API 密钥", "xpack.enterpriseSearch.pageTemplate.endpointsButtonLabel": "终端和 API 密钥", "xpack.enterpriseSearch.passwordLabel": "密码", "xpack.enterpriseSearch.pipeline.title": "转换和扩充数据", @@ -38149,7 +38142,6 @@ "xpack.serverlessSearch.indexManagement.indexDetails.overview.aliasesFlyout.title": "{indexName} 别名", "xpack.serverlessSearch.indexManagement.indexDetails.overview.emptyPrompt.api.body": "定制这些变量以匹配您的内容。如需完整的设置指南,请访问我们的{getStartedLink}指南。", "xpack.serverlessSearch.indexManagement.indexDetails.overview.emptyPrompt.body": "使用 {logstashLink}、{beatsLink}、{connectorsLink} 或 RESTful {apiCallsLink} 为您的索引填充数据。", - "xpack.serverlessSearch.indexManagement.indexDetails.overview.storagePanel.documentCount": "{documentCount} 个文档/{deletedCount} 个已删除", "xpack.serverlessSearch.searchConnectors.configurationConnector.config.documentation.description": "此连接器支持几种身份验证方法。请联系管理员获取正确的连接凭据。{documentationUrl}", "xpack.serverlessSearch.apiKey.apiKeyStepDescription": "此密钥仅显示一次,因此请将其保存到某个安全位置。我们不存储您的 API 密钥,因此,如果您丢失了密钥,则需要生成替代密钥。", "xpack.serverlessSearch.apiKey.apiKeyStepTitle": "存储此 API 密钥", @@ -38275,9 +38267,6 @@ "xpack.serverlessSearch.indexManagement.indexDetails.overview.error.description": "加载索引时出错。", "xpack.serverlessSearch.indexManagement.indexDetails.overview.error.title": "无法加载索引", "xpack.serverlessSearch.indexManagement.indexDetails.overview.loading.title": "正在加载索引", - "xpack.serverlessSearch.indexManagement.indexDetails.overview.storagePanel.primaryLabel": "主分片", - "xpack.serverlessSearch.indexManagement.indexDetails.overview.storagePanel.title": "存储", - "xpack.serverlessSearch.indexManagement.indexDetails.overview.storagePanel.totalLabel": "合计", "xpack.serverlessSearch.indexManagementTab.documents": "文档", "xpack.serverlessSearch.indexManagementTab.documents.noMappings": "找不到索引的文档", "xpack.serverlessSearch.indexMappings.ingestPipelinesDocs.description": "想要添加定制字段,或使用已训练 ML 模型分析并扩充您的已索引文档?使用特定于索引的采集管道根据您的需求来定制文档。", diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/notify_badge/notify_badge.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/notify_badge/notify_badge.tsx index e09892471419b..72344122d90f2 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/notify_badge/notify_badge.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/notify_badge/notify_badge.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useCallback, useMemo, useState } from 'react'; +import React, { useCallback, useMemo, useState, useRef } from 'react'; import moment from 'moment'; import { EuiButton, @@ -91,6 +91,8 @@ export const RulesListNotifyBadge: React.FunctionComponent(null); + const { notifications: { toasts }, } = useKibana().services; @@ -192,6 +194,7 @@ export const RulesListNotifyBadge: React.FunctionComponent {formattedSnoozeText} @@ -210,6 +213,7 @@ export const RulesListNotifyBadge: React.FunctionComponent {formattedSnoozeText} @@ -232,6 +236,7 @@ export const RulesListNotifyBadge: React.FunctionComponent ); }, [isPopoverOpen, isLoading, isDisabled, showOnHover, openPopover]); @@ -248,6 +253,7 @@ export const RulesListNotifyBadge: React.FunctionComponent ); }, [isLoading, isDisabled, openPopover]); @@ -311,9 +317,10 @@ export const RulesListNotifyBadge: React.FunctionComponent focusTrapButtonRef.current?.focus()); } }, - [setRequestInFlightLoading, snoozeRule, onRuleChanged, toasts, closePopover] + [closePopover, snoozeRule, onRuleChanged, toasts] ); const onApplyUnsnooze = useCallback( @@ -328,9 +335,10 @@ export const RulesListNotifyBadge: React.FunctionComponent focusTrapButtonRef.current?.focus()); } }, - [setRequestInFlightLoading, unsnoozeRule, onRuleChanged, toasts, closePopover] + [closePopover, unsnoozeRule, onRuleChanged, toasts] ); const popover = ( diff --git a/x-pack/test/accessibility/apps/group3/ml_embeddables_in_dashboard.ts b/x-pack/test/accessibility/apps/group3/ml_embeddables_in_dashboard.ts index c3278b39096c7..39a10547c57b9 100644 --- a/x-pack/test/accessibility/apps/group3/ml_embeddables_in_dashboard.ts +++ b/x-pack/test/accessibility/apps/group3/ml_embeddables_in_dashboard.ts @@ -81,7 +81,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); for (const testData of testDataList) { - describe(testData.suiteSuffix, function () { + // FLAKY: https://github.com/elastic/kibana/issues/183196 + describe.skip(testData.suiteSuffix, function () { before(async () => { await ml.api.createAndRunAnomalyDetectionLookbackJob( testData.jobConfig, diff --git a/x-pack/test/alerting_api_integration/common/lib/wait_for_execution_count.ts b/x-pack/test/alerting_api_integration/common/lib/wait_for_execution_count.ts index 6ad0dab431d81..3cd95484e1d5c 100644 --- a/x-pack/test/alerting_api_integration/common/lib/wait_for_execution_count.ts +++ b/x-pack/test/alerting_api_integration/common/lib/wait_for_execution_count.ts @@ -13,7 +13,7 @@ async function delay(millis: number): Promise { } export function createWaitForExecutionCount( - st: supertest.SuperTest, + st: supertest.Agent, spaceId?: string, delayMs: number = 3000 ) { 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 bd6c7761a5fcd..1a66aa2fcd9ed 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 @@ -6,7 +6,7 @@ */ import type { Client } from '@elastic/elasticsearch'; -import type { SuperTest, Test } from 'supertest'; +import type { Agent as SuperTestAgent } from 'supertest'; import { ToolingLog } from '@kbn/tooling-log'; import { ThresholdParams } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; import { refreshSavedObjectIndices } from './refresh_index'; @@ -17,7 +17,7 @@ export async function createIndexConnector({ indexName, logger, }: { - supertest: SuperTest; + supertest: SuperTestAgent; name: string; indexName: string; logger: ToolingLog; @@ -51,7 +51,7 @@ export async function createRule({ logger, esClient, }: { - supertest: SuperTest; + supertest: SuperTestAgent; ruleTypeId: string; name: string; params: Params; diff --git a/x-pack/test/alerting_api_integration/observability/helpers/alerting_wait_for_helpers.ts b/x-pack/test/alerting_api_integration/observability/helpers/alerting_wait_for_helpers.ts index 63960b222cede..032435a03e007 100644 --- a/x-pack/test/alerting_api_integration/observability/helpers/alerting_wait_for_helpers.ts +++ b/x-pack/test/alerting_api_integration/observability/helpers/alerting_wait_for_helpers.ts @@ -29,7 +29,7 @@ export async function waitForRuleStatus({ }: { id: string; expectedStatus: string; - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; retryService: RetryService; logger: ToolingLog; }): Promise> { 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 index 1f84b2556ab70..3fdceed845856 100644 --- a/x-pack/test/alerting_api_integration/observability/helpers/data_view.ts +++ b/x-pack/test/alerting_api_integration/observability/helpers/data_view.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SuperTest, Test } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { ToolingLog } from '@kbn/tooling-log'; export const createDataView = async ({ @@ -15,7 +15,7 @@ export const createDataView = async ({ title, logger, }: { - supertest: SuperTest; + supertest: SuperTestAgent; id: string; name: string; title: string; @@ -50,7 +50,7 @@ export const deleteDataView = async ({ id, logger, }: { - supertest: SuperTest; + supertest: SuperTestAgent; id: string; logger: ToolingLog; }) => { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/backfill/task_runner.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/backfill/task_runner.ts index 6b555198e5eff..aa81a4713a8af 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/backfill/task_runner.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/backfill/task_runner.ts @@ -92,7 +92,7 @@ export default function createBackfillTaskRunnerTests({ getService }: FtrProvide await esTestIndexTool.setup(); }); afterEach(async () => { - objectRemover.removeAll(); + await objectRemover.removeAll(); await esTestIndexTool.destroy(); }); after(async () => { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find.ts index 9816059995142..e7461476a2996 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { SuperTest, Test } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { chunk, omit } from 'lodash'; import { v4 as uuidv4 } from 'uuid'; import { SuperuserAtSpace1, UserAtSpaceScenarios } from '../../../scenarios'; @@ -16,11 +16,13 @@ import { FtrProviderContext } from '../../../../common/ftr_provider_context'; const findTestUtils = ( describeType: 'internal' | 'public', objectRemover: ObjectRemover, - supertest: SuperTest, + supertest: SuperTestAgent, supertestWithoutAuth: any ) => { - describe.skip(describeType, () => { - afterEach(() => objectRemover.removeAll()); + describe(describeType, () => { + afterEach(async () => { + await objectRemover.removeAll(); + }); for (const scenario of UserAtSpaceScenarios) { const { user, space } = scenario; @@ -651,12 +653,12 @@ export default function createFindTests({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const supertestWithoutAuth = getService('supertestWithoutAuth'); - // Failing: See https://github.com/elastic/kibana/issues/182263 - // Failing: See https://github.com/elastic/kibana/issues/182284 - describe.skip('find', () => { + describe('find', () => { const objectRemover = new ObjectRemover(supertest); - afterEach(() => objectRemover.removeAll()); + afterEach(async () => { + await objectRemover.removeAll(); + }); findTestUtils('public', objectRemover, supertest, supertestWithoutAuth); findTestUtils('internal', objectRemover, supertest, supertestWithoutAuth); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find_with_post.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find_with_post.ts index 7b60595c4d66e..b407ee072a78b 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find_with_post.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find_with_post.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { SuperTest, Test } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { chunk, omit } from 'lodash'; import { v4 as uuidv4 } from 'uuid'; import { UserAtSpaceScenarios } from '../../../scenarios'; @@ -16,17 +16,17 @@ import { FtrProviderContext } from '../../../../common/ftr_provider_context'; const findTestUtils = ( describeType: 'internal' | 'public', objectRemover: ObjectRemover, - supertest: SuperTest, + supertest: SuperTestAgent, supertestWithoutAuth: any ) => { - // FLAKY: https://github.com/elastic/kibana/issues/182314 - describe.skip(describeType, () => { - afterEach(() => objectRemover.removeAll()); + describe(describeType, () => { + afterEach(async () => { + await objectRemover.removeAll(); + }); for (const scenario of UserAtSpaceScenarios) { const { user, space } = scenario; - // FLAKY: https://github.com/elastic/kibana/issues/182314 - describe.skip(scenario.id, () => { + describe(scenario.id, () => { it('should handle find alert request appropriately', async () => { const { body: createdAlert } = await supertest .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) @@ -581,7 +581,9 @@ export default function createFindTests({ getService }: FtrProviderContext) { describe('find with post', () => { const objectRemover = new ObjectRemover(supertest); - afterEach(() => objectRemover.removeAll()); + afterEach(async () => { + await objectRemover.removeAll(); + }); findTestUtils('internal', objectRemover, supertest, supertestWithoutAuth); }); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get.ts index 6d245ed28cc00..fa362249229e3 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { SuperTest, Test } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { SuperuserAtSpace1, UserAtSpaceScenarios } from '../../../scenarios'; import { getUrlPrefix, @@ -19,7 +19,7 @@ import { FtrProviderContext } from '../../../../common/ftr_provider_context'; const getTestUtils = ( describeType: 'internal' | 'public', objectRemover: ObjectRemover, - supertest: SuperTest, + supertest: SuperTestAgent, supertestWithoutAuth: any ) => { describe(describeType, () => { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/crowdstrike.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/crowdstrike.ts index 7b4d064eb6b6e..c9d1ca8814592 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/crowdstrike.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/crowdstrike.ts @@ -131,7 +131,7 @@ export default function createCrowdstrikeTests({ getService }: FtrProviderContex password = 'changeme', errorLogger = logErrorDetails, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; subAction: string; subActionParams: Record; expectedHttpCode?: number; diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/sentinelone.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/sentinelone.ts index 4b184ccb7a595..bf6f88be08fdf 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/sentinelone.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/sentinelone.ts @@ -140,7 +140,7 @@ export default function createSentinelOneTests({ getService }: FtrProviderContex password = 'changeme', errorLogger = logErrorDetails, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; subAction: string; subActionParams: Record; expectedHttpCode?: number; diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/sub_action_framework/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/sub_action_framework/index.ts index 0a363e001535b..85e3b8405257f 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/sub_action_framework/index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/sub_action_framework/index.ts @@ -22,7 +22,7 @@ const createSubActionConnector = async ({ connectorTypeId = 'test.sub-action-connector', expectedHttpCode = 200, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; config?: Record; secrets?: Record; connectorTypeId?: string; @@ -56,7 +56,7 @@ const executeSubAction = async ({ subActionParams, expectedHttpCode = 200, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; connectorId: string; subAction: string; subActionParams: Record; diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/connector_types/stack/webhook.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/connector_types/stack/webhook.ts index f3b3a4e7076ef..ad6a8d12baed3 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/connector_types/stack/webhook.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/connector_types/stack/webhook.ts @@ -8,7 +8,7 @@ import http from 'http'; import https from 'https'; import getPort from 'get-port'; -import { SuperTest, Test } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import expect from '@kbn/expect'; import { URL, format as formatUrl } from 'url'; import { @@ -177,7 +177,7 @@ export default function webhookTest({ getService }: FtrProviderContext) { } export async function createWebhookAction( - supertest: SuperTest, + supertest: SuperTestAgent, webhookSimulatorURL: string, config: Record> = {} ): Promise { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/find.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/find.ts index a83ec1e70a0a1..d38bb2f40a275 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/find.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/find.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { SuperTest, Test } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { fromKueryExpression } from '@kbn/es-query'; import { Spaces } from '../../../scenarios'; import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../../common/lib'; @@ -14,7 +14,7 @@ import { FtrProviderContext } from '../../../../common/ftr_provider_context'; async function createAlert( objectRemover: ObjectRemover, - supertest: SuperTest, + supertest: SuperTestAgent, overwrites = {} ) { const { body: createdAlert } = await supertest @@ -28,7 +28,7 @@ async function createAlert( const findTestUtils = ( describeType: 'internal' | 'public', - supertest: SuperTest, + supertest: SuperTestAgent, objectRemover: ObjectRemover ) => { describe(describeType, () => { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/get.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/get.ts index 51f246e2c5609..c22e8233f2d73 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/get.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/get.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { SuperTest, Test } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { Spaces } from '../../../scenarios'; import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../../common/lib'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; @@ -14,7 +14,7 @@ import { FtrProviderContext } from '../../../../common/ftr_provider_context'; const getTestUtils = ( describeType: 'internal' | 'public', objectRemover: ObjectRemover, - supertest: SuperTest + supertest: SuperTestAgent ) => { describe(describeType, () => { afterEach(() => objectRemover.removeAll()); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/test_helpers.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/test_helpers.ts index a898cc14b9104..b2196d9ea3724 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/test_helpers.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/test_helpers.ts @@ -8,7 +8,7 @@ import moment from 'moment'; import type { RetryService } from '@kbn/ftr-common-functional-services'; import type { IValidatedEvent } from '@kbn/event-log-plugin/server'; -import type { SuperTest, Test } from 'supertest'; +import type { Agent as SuperTestAgent } from 'supertest'; import expect from '@kbn/expect'; import type { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { getUrlPrefix, getTestRuleData, ObjectRemover, getEventLog } from '../../../../common/lib'; @@ -23,7 +23,7 @@ export const createRule = async ({ }: { actionId: string; pattern: { instance: boolean[] }; - supertest: SuperTest; + supertest: SuperTestAgent; objectRemover: ObjectRemover; overwrites?: any; }) => { @@ -65,7 +65,7 @@ export const createAction = async ({ supertest, objectRemover, }: { - supertest: SuperTest; + supertest: SuperTestAgent; objectRemover: ObjectRemover; }) => { const { body: createdAction } = await supertest @@ -89,7 +89,7 @@ export const createMaintenanceWindow = async ({ objectRemover, }: { overwrites?: any; - supertest: SuperTest; + supertest: SuperTestAgent; objectRemover: ObjectRemover; }) => { const { body: window } = await supertest @@ -112,11 +112,7 @@ export const createMaintenanceWindow = async ({ return window; }; -export const getActiveMaintenanceWindows = async ({ - supertest, -}: { - supertest: SuperTest; -}) => { +export const getActiveMaintenanceWindows = async ({ supertest }: { supertest: SuperTestAgent }) => { const { body: activeMaintenanceWindows } = await supertest .get(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rules/maintenance_window/_active`) .set('kbn-xsrf', 'foo') @@ -130,7 +126,7 @@ export const finishMaintenanceWindow = async ({ supertest, }: { id: string; - supertest: SuperTest; + supertest: SuperTestAgent; }) => { return supertest .post( @@ -188,7 +184,7 @@ export const expectNoActionsFired = async ({ retry, }: { id: string; - supertest: SuperTest; + supertest: SuperTestAgent; retry: RetryService; }) => { const events = await retry.try(async () => { @@ -215,7 +211,7 @@ export const runSoon = async ({ retry, }: { id: string; - supertest: SuperTest; + supertest: SuperTestAgent; retry: RetryService; }) => { return retry.try(async () => { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alert_delay.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alert_delay.ts index 7062c1c65fd9c..e3f30d252495a 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alert_delay.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alert_delay.ts @@ -43,7 +43,7 @@ export default function createAlertDelayTests({ getService }: FtrProviderContext instance: [true, true, true, false, true], }; - const ruleId = await createRule(actionId, pattern, 20); + const ruleId = await createRule(actionId, pattern, 1); objectRemover.add(space.id, ruleId, 'rule', 'alerting'); let state = await getAlertState(start, ruleId); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data/alerts_as_data_alert_delay.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data/alerts_as_data_alert_delay.ts index 991ed513ee984..2bb97a60bf0c2 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data/alerts_as_data_alert_delay.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data/alerts_as_data_alert_delay.ts @@ -80,16 +80,17 @@ export default function createAlertsAsDataAlertDelayInstallResourcesTest({ conflicts: 'proceed', }); }); - afterEach(() => objectRemover.removeAll()); - after(async () => { - await objectRemover.removeAll(); - await esTestIndexTool.destroy(); + afterEach(async () => { + objectRemover.removeAll(); await es.deleteByQuery({ index: [alertsAsDataIndex, alwaysFiringAlertsAsDataIndex], query: { match_all: {} }, conflicts: 'proceed', }); }); + after(async () => { + await esTestIndexTool.destroy(); + }); it('should generate expected events with a alertDelay with AAD', async () => { const { body: createdAction } = await supertestWithoutAuth @@ -620,6 +621,138 @@ export default function createAlertsAsDataAlertDelayInstallResourcesTest({ // alert consecutive matches should match the active count expect(source[ALERT_CONSECUTIVE_MATCHES]).to.equal(4); }); + + it('should not recover alert if the activeCount did not reach the alertDelay threshold with AAD', async () => { + const { body: createdAction } = await supertestWithoutAuth + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'MY action', + connector_type_id: 'test.noop', + config: {}, + secrets: {}, + }) + .expect(200); + + // pattern of when the alert should fire + const pattern = { + instance: [true, false, true], + }; + + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + rule_type_id: 'test.patternFiringAad', + schedule: { interval: '1d' }, + throttle: null, + notify_when: null, + params: { + pattern, + }, + actions: [ + { + id: createdAction.id, + group: 'default', + params: {}, + frequency: { + summary: false, + throttle: null, + notify_when: RuleNotifyWhen.CHANGE, + }, + }, + ], + alert_delay: { + active: 3, + }, + }) + ); + + expect(response.status).to.eql(200); + const ruleId = response.body.id; + objectRemover.add(Spaces.space1.id, ruleId, 'rule', 'alerting'); + + // -------------------------- + // RUN 1 - 0 new alerts + // -------------------------- + let events: IValidatedEvent[] = await waitForEventLogDocs( + ruleId, + new Map([['execute', { equal: 1 }]]) + ); + let executeEvent = events[0]; + expect(get(executeEvent, ACTIVE_PATH)).to.be(0); + expect(get(executeEvent, NEW_PATH)).to.be(0); + expect(get(executeEvent, RECOVERED_PATH)).to.be(0); + expect(get(executeEvent, ACTION_PATH)).to.be(0); + expect(get(executeEvent, DELAYED_PATH)).to.be(1); + + // Query for alerts + const alertDocsRun1 = await queryForAlertDocs(); + + // Get alert state from task document + let state: any = await getTaskState(ruleId); + expect(state.alertInstances.instance.meta.activeCount).to.equal(1); + expect(state.alertInstances.instance.state.patternIndex).to.equal(0); + + // After the first run, we should have 0 alert docs for the 0 active alerts + expect(alertDocsRun1.length).to.equal(0); + + // -------------------------- + // RUN 2 - 0 new alerts + // -------------------------- + let runSoon = await supertestWithoutAuth + .post(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rule/${ruleId}/_run_soon`) + .set('kbn-xsrf', 'foo'); + expect(runSoon.status).to.eql(204); + + events = await waitForEventLogDocs(ruleId, new Map([['execute', { equal: 2 }]])); + executeEvent = events[1]; + expect(get(executeEvent, ACTIVE_PATH)).to.be(0); + expect(get(executeEvent, NEW_PATH)).to.be(0); + expect(get(executeEvent, RECOVERED_PATH)).to.be(0); + expect(get(executeEvent, ACTION_PATH)).to.be(0); + expect(get(executeEvent, DELAYED_PATH)).to.be(0); + + // Query for alerts + const alertDocsRun2 = await queryForAlertDocs(); + + // Get alert state from task document + state = await getTaskState(ruleId); + expect(state.alertInstances).to.eql({}); + expect(state.alertRecoveredInstances).to.eql({}); + expect(state.alertTypeState.patternIndex).to.equal(2); + + // After the second run, we should have 0 alert docs for the 0 recovered alerts + expect(alertDocsRun2.length).to.equal(0); + + // -------------------------- + // RUN 3 - 0 new alerts + // -------------------------- + runSoon = await supertestWithoutAuth + .post(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rule/${ruleId}/_run_soon`) + .set('kbn-xsrf', 'foo'); + expect(runSoon.status).to.eql(204); + + events = await waitForEventLogDocs(ruleId, new Map([['execute', { equal: 3 }]])); + executeEvent = events[2]; + expect(get(executeEvent, ACTIVE_PATH)).to.be(0); + expect(get(executeEvent, NEW_PATH)).to.be(0); + expect(get(executeEvent, RECOVERED_PATH)).to.be(0); + expect(get(executeEvent, ACTION_PATH)).to.be(0); + expect(get(executeEvent, DELAYED_PATH)).to.be(1); + + // Query for alerts + const alertDocsRun3 = await queryForAlertDocs(); + + // Get alert state from task document + state = await getTaskState(ruleId); + expect(state.alertInstances.instance.meta.activeCount).to.equal(1); + expect(state.alertInstances.instance.state.patternIndex).to.equal(2); + + // After the third run, we should have 0 alert docs for the 0 active alerts + expect(alertDocsRun3.length).to.equal(0); + }); }); function testExpectRuleData( diff --git a/x-pack/test/api_integration/apis/asset_manager/tests/helpers.ts b/x-pack/test/api_integration/apis/asset_manager/tests/helpers.ts index 3d1086ca8b8e4..94ab2f5b99ffe 100644 --- a/x-pack/test/api_integration/apis/asset_manager/tests/helpers.ts +++ b/x-pack/test/api_integration/apis/asset_manager/tests/helpers.ts @@ -9,11 +9,11 @@ import type { AssetWithoutTimestamp } from '@kbn/assetManager-plugin/common/type import type { WriteSamplesPostBody } from '@kbn/assetManager-plugin/server'; import { apm, infra, timerange } from '@kbn/apm-synthtrace-client'; import expect from '@kbn/expect'; -import { SuperTest, Test } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; const SAMPLE_ASSETS_ENDPOINT = '/api/asset-manager/assets/sample'; -export type KibanaSupertest = SuperTest; +export type KibanaSupertest = SuperTestAgent; // NOTE: In almost every case in tests, you want { refresh: true } // in the options of this function, so it is defaulted to that value. diff --git a/x-pack/test/api_integration/apis/cloud_security_posture/helper.ts b/x-pack/test/api_integration/apis/cloud_security_posture/helper.ts index 03a61807f1b31..5565e7a6e096b 100644 --- a/x-pack/test/api_integration/apis/cloud_security_posture/helper.ts +++ b/x-pack/test/api_integration/apis/cloud_security_posture/helper.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { SuperTest, Test } from 'supertest'; +import type { Agent as SuperTestAgent } from 'supertest'; import { Client } from '@elastic/elasticsearch'; import expect from '@kbn/expect'; import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; @@ -43,7 +43,7 @@ export const addIndex = async (es: Client, findingsMock: T[], indexName: stri }; export async function createPackagePolicy( - supertest: SuperTest, + supertest: SuperTestAgent, agentPolicyId: string, policyTemplate: string, input: string, diff --git a/x-pack/test/api_integration/apis/management/index_management/lib/templates.api.ts b/x-pack/test/api_integration/apis/management/index_management/lib/templates.api.ts index e929cbff2f188..bae578e6c0490 100644 --- a/x-pack/test/api_integration/apis/management/index_management/lib/templates.api.ts +++ b/x-pack/test/api_integration/apis/management/index_management/lib/templates.api.ts @@ -36,7 +36,9 @@ export function templatesApi(getService: FtrProviderContext['getService']) { .send(payload); // Delete all templates created during tests - const cleanUpTemplates = async (additionalRequestHeaders: object = {}) => { + const cleanUpTemplates = async ( + additionalRequestHeaders: Record = {} + ) => { try { await deleteTemplates(templatesCreated).set(additionalRequestHeaders); templatesCreated = []; diff --git a/x-pack/test/api_integration/apis/metrics_ui/helpers.ts b/x-pack/test/api_integration/apis/metrics_ui/helpers.ts index b5375b8d70cab..427909e10ac94 100644 --- a/x-pack/test/api_integration/apis/metrics_ui/helpers.ts +++ b/x-pack/test/api_integration/apis/metrics_ui/helpers.ts @@ -6,9 +6,6 @@ */ import { apm, timerange } from '@kbn/apm-synthtrace-client'; -import { SuperTest, Test } from 'supertest'; - -export type KibanaSupertest = SuperTest; // generates traces, metrics for services export function generateServicesData({ diff --git a/x-pack/test/api_integration/apis/slos/fetch_historical_summary.ts b/x-pack/test/api_integration/apis/slos/fetch_historical_summary.ts new file mode 100644 index 0000000000000..94cccf1e14938 --- /dev/null +++ b/x-pack/test/api_integration/apis/slos/fetch_historical_summary.ts @@ -0,0 +1,131 @@ +/* + * Copyright 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 { + SLO_DESTINATION_INDEX_NAME, + SLO_DESTINATION_INDEX_PATTERN, +} from '@kbn/slo-plugin/common/constants'; +import { ALL_VALUE } from '@kbn/slo-schema'; +import moment from 'moment'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const esClient = getService('es'); + const esDeleteAllIndices = getService('esDeleteAllIndices'); + const sloApi = getService('slo'); + + const SLO_ID = 'slo-fake-1'; + // Failing: See https://github.com/elastic/kibana/issues/183750 + describe.skip('fetch historical summary', () => { + before(async () => { + const now = moment().startOf('minute'); + const curr = now.clone().subtract(30, 'days'); + const end = now.clone().add(5, 'minutes'); + + const batchOperations = []; + while (curr.isSameOrBefore(end)) { + batchOperations.push([ + { index: { _index: SLO_DESTINATION_INDEX_NAME } }, + { + '@timestamp': curr.toISOString(), + slo: { + id: SLO_ID, + revision: 1, + instanceId: ALL_VALUE, + numerator: 90, + denominator: 100, + isGoodSlice: 1, + groupings: {}, + }, + }, + ]); + curr.add(1, 'minute'); + } + + await esClient.bulk({ + index: SLO_DESTINATION_INDEX_NAME, + operations: batchOperations.flat(), + refresh: 'wait_for', + }); + + await esClient.indices.refresh({ index: SLO_DESTINATION_INDEX_NAME }); + }); + + after(async () => { + await esDeleteAllIndices(SLO_DESTINATION_INDEX_PATTERN); + }); + + it('computes the historical summary for a rolling occurrences SLO', async () => { + const response = await sloApi.fetchHistoricalSummary({ + list: [ + { + sloId: SLO_ID, + instanceId: ALL_VALUE, + timeWindow: { + duration: '7d', + type: 'rolling', + }, + budgetingMethod: 'occurrences', + objective: { + target: 0.9, + }, + groupBy: ALL_VALUE, + revision: 1, + }, + ], + }); + expect(response[0].sloId).to.eql(SLO_ID); + expect(response[0].instanceId).to.eql(ALL_VALUE); + expect(response[0].data).to.have.length(168); // 7 days * 24 hours/day * 1 bucket/hour + const last = response[0].data.pop(); + expect(last?.errorBudget).to.eql({ + consumed: 1, + initial: 0.1, + isEstimated: false, + remaining: 0, + }); + expect(last?.sliValue).to.eql(0.9); + expect(last?.status).to.eql('HEALTHY'); + }); + + it('computes the historical summary for a rolling timeslices SLO', async () => { + const response = await sloApi.fetchHistoricalSummary({ + list: [ + { + sloId: SLO_ID, + instanceId: ALL_VALUE, + timeWindow: { + duration: '7d', + type: 'rolling', + }, + budgetingMethod: 'timeslices', + objective: { + target: 0.9, + timesliceTarget: 0.8, + timesliceWindow: '1m', + }, + groupBy: ALL_VALUE, + revision: 1, + }, + ], + }); + expect(response[0].sloId).to.eql(SLO_ID); + expect(response[0].instanceId).to.eql(ALL_VALUE); + expect(response[0].data).to.have.length(168); // 7 days * 24 hours/day * 1 bucket/hour + const last = response[0].data.pop(); + expect(last?.errorBudget).to.eql({ + consumed: 0, + initial: 0.1, + isEstimated: false, + remaining: 1, + }); + expect(last?.sliValue).to.eql(1); + expect(last?.status).to.eql('HEALTHY'); + }); + }); +} diff --git a/x-pack/test/api_integration/apis/slos/index.ts b/x-pack/test/api_integration/apis/slos/index.ts index 6276dd4a4cf6f..c80a0c58e5ecc 100644 --- a/x-pack/test/api_integration/apis/slos/index.ts +++ b/x-pack/test/api_integration/apis/slos/index.ts @@ -14,5 +14,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./get_slo')); loadTestFile(require.resolve('./update_slo')); loadTestFile(require.resolve('./reset_slo')); + loadTestFile(require.resolve('./fetch_historical_summary')); }); } diff --git a/x-pack/test/api_integration/apis/telemetry/telemetry.ts b/x-pack/test/api_integration/apis/telemetry/telemetry.ts index 3652b32d9a372..203189906f812 100644 --- a/x-pack/test/api_integration/apis/telemetry/telemetry.ts +++ b/x-pack/test/api_integration/apis/telemetry/telemetry.ts @@ -50,7 +50,7 @@ function getCacheDetails(body: UnencryptedTelemetryPayload): CacheDetails[] { * @param timestamp The new timestamp to be set */ function updateMonitoringDates( - esSupertest: SuperTest.SuperTest, + esSupertest: SuperTest.Agent, fromTimestamp: string, toTimestamp: string, timestamp: string diff --git a/x-pack/test/api_integration/services/slo.ts b/x-pack/test/api_integration/services/slo.ts index 4d28786df56a5..c7b765dddf613 100644 --- a/x-pack/test/api_integration/services/slo.ts +++ b/x-pack/test/api_integration/services/slo.ts @@ -5,11 +5,21 @@ * 2.0. */ -import { CreateSLOInput, FindSLODefinitionsResponse } from '@kbn/slo-schema'; import { SLO_SUMMARY_DESTINATION_INDEX_NAME } from '@kbn/slo-plugin/common/constants'; +import { + CreateSLOInput, + fetchHistoricalSummaryParamsSchema, + FetchHistoricalSummaryResponse, + FindSLODefinitionsResponse, +} from '@kbn/slo-schema'; +import * as t from 'io-ts'; import { waitForIndexToBeEmpty } from '../apis/slos/helper/wait_for_index_state'; import { FtrProviderContext } from '../ftr_provider_context'; +type FetchHistoricalSummaryParams = t.OutputOf< + typeof fetchHistoricalSummaryParamsSchema.props.body +>; + export function SloApiProvider({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const esClient = getService('es'); @@ -49,5 +59,16 @@ export function SloApiProvider({ getService }: FtrProviderContext) { } await waitForIndexToBeEmpty({ esClient, indexName: SLO_SUMMARY_DESTINATION_INDEX_NAME }); }, + async fetchHistoricalSummary( + params: FetchHistoricalSummaryParams + ): Promise { + const { body } = await supertest + .post(`/internal/observability/slos/_historical_summary`) + .set('kbn-xsrf', 'foo') + .set('elastic-api-version', '1') + .send(params); + + return body; + }, }; } diff --git a/x-pack/test/apm_api_integration/common/apm_api_supertest.ts b/x-pack/test/apm_api_integration/common/apm_api_supertest.ts index 9e4f25a7c7c7b..330de33dc7ab6 100644 --- a/x-pack/test/apm_api_integration/common/apm_api_supertest.ts +++ b/x-pack/test/apm_api_integration/common/apm_api_supertest.ts @@ -15,7 +15,7 @@ import type { import type { APIEndpoint } from '@kbn/apm-plugin/server'; import { formatRequest } from '@kbn/server-route-repository'; -export function createApmApiClient(st: supertest.SuperTest) { +export function createApmApiClient(st: supertest.Agent) { return async ( options: { type?: 'form-data'; diff --git a/x-pack/test/apm_api_integration/common/bettertest.ts b/x-pack/test/apm_api_integration/common/bettertest.ts index e1132be3f9a77..8665d200fd15f 100644 --- a/x-pack/test/apm_api_integration/common/bettertest.ts +++ b/x-pack/test/apm_api_integration/common/bettertest.ts @@ -30,7 +30,7 @@ export interface BetterTestResponse { * This is useful for tests that expect a 200 response * It also makes it easier to debug tests that fail because of a 500 response. */ -export function getBettertest(st: supertest.SuperTest) { +export function getBettertest(st: supertest.Agent) { return async ({ pathname, method = 'get', diff --git a/x-pack/test/apm_api_integration/tests/alerts/helpers/alerting_api_helper.ts b/x-pack/test/apm_api_integration/tests/alerts/helpers/alerting_api_helper.ts index e4cc8d302ba4a..5da6ee4f860d0 100644 --- a/x-pack/test/apm_api_integration/tests/alerts/helpers/alerting_api_helper.ts +++ b/x-pack/test/apm_api_integration/tests/alerts/helpers/alerting_api_helper.ts @@ -8,7 +8,7 @@ import { Client, errors } from '@elastic/elasticsearch'; import { ParsedTechnicalFields } from '@kbn/rule-registry-plugin/common'; import pRetry from 'p-retry'; -import type { SuperTest, Test } from 'supertest'; +import type { Agent as SuperTestAgent } from 'supertest'; import { ApmRuleType } from '@kbn/rule-data-utils'; import { ApmRuleParamsType } from '@kbn/apm-plugin/common/rules/schema'; import { ApmDocumentType } from '@kbn/apm-plugin/common/document_type'; @@ -26,7 +26,7 @@ export async function createApmRule({ params, actions = [], }: { - supertest: SuperTest; + supertest: SuperTestAgent; ruleTypeId: T; name: string; params: ApmRuleParamsType[T]; @@ -111,7 +111,7 @@ export async function runRuleSoon({ supertest, }: { ruleId: string; - supertest: SuperTest; + supertest: SuperTestAgent; }): Promise> { return pRetry( async () => { @@ -143,13 +143,13 @@ export async function deleteRuleById({ supertest, ruleId, }: { - supertest: SuperTest; + supertest: SuperTestAgent; ruleId: string; }) { await supertest.delete(`/api/alerting/rule/${ruleId}`).set('kbn-xsrf', 'foo'); } -export async function deleteApmRules(supertest: SuperTest) { +export async function deleteApmRules(supertest: SuperTestAgent) { const res = await supertest.get( `/api/alerting/rules/_find?filter=alert.attributes.consumer:apm&per_page=10000` ); @@ -180,7 +180,7 @@ export async function createIndexConnector({ supertest, name, }: { - supertest: SuperTest; + supertest: SuperTestAgent; name: string; }) { const { body } = await supertest @@ -228,7 +228,7 @@ export async function deleteAllActionConnectors({ supertest, es, }: { - supertest: SuperTest; + supertest: SuperTestAgent; es: Client; }): Promise { const res = await supertest.get(`/api/actions/connectors`); @@ -245,7 +245,7 @@ async function deleteActionConnector({ supertest, actionId, }: { - supertest: SuperTest; + supertest: SuperTestAgent; actionId: string; }) { return supertest.delete(`/api/actions/connector/${actionId}`).set('kbn-xsrf', 'foo'); diff --git a/x-pack/test/apm_api_integration/tests/alerts/helpers/cleanup_rule_and_alert_state.ts b/x-pack/test/apm_api_integration/tests/alerts/helpers/cleanup_rule_and_alert_state.ts index ae97266ee084c..2fae6c9643ff7 100644 --- a/x-pack/test/apm_api_integration/tests/alerts/helpers/cleanup_rule_and_alert_state.ts +++ b/x-pack/test/apm_api_integration/tests/alerts/helpers/cleanup_rule_and_alert_state.ts @@ -7,7 +7,7 @@ import { Client } from '@elastic/elasticsearch'; import { ToolingLog } from '@kbn/tooling-log'; -import type { SuperTest, Test } from 'supertest'; +import type { Agent as SuperTestAgent } from 'supertest'; import { clearKibanaApmEventLog, deleteApmRules, @@ -22,7 +22,7 @@ export async function cleanupRuleAndAlertState({ logger, }: { es: Client; - supertest: SuperTest; + supertest: SuperTestAgent; logger: ToolingLog; }) { try { diff --git a/x-pack/test/apm_api_integration/tests/alerts/helpers/wait_for_active_rule.ts b/x-pack/test/apm_api_integration/tests/alerts/helpers/wait_for_active_rule.ts index 29dc20840cec5..a2ee7253e9b8c 100644 --- a/x-pack/test/apm_api_integration/tests/alerts/helpers/wait_for_active_rule.ts +++ b/x-pack/test/apm_api_integration/tests/alerts/helpers/wait_for_active_rule.ts @@ -17,7 +17,7 @@ export async function waitForActiveRule({ logger, }: { ruleId: string; - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; logger?: ToolingLog; }): Promise> { return pRetry( diff --git a/x-pack/test/cases_api_integration/common/lib/alerts.ts b/x-pack/test/cases_api_integration/common/lib/alerts.ts index a74524c448493..8df074b0d3474 100644 --- a/x-pack/test/cases_api_integration/common/lib/alerts.ts +++ b/x-pack/test/cases_api_integration/common/lib/alerts.ts @@ -30,7 +30,7 @@ import { createComment, deleteAllComments } from './api'; import { postCaseReq } from './mock'; export const createSecuritySolutionAlerts = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, numberOfSignals: number = 1 ): Promise> => { @@ -47,7 +47,7 @@ export const createSecuritySolutionAlerts = async ( }; export const getSecuritySolutionAlerts = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, alertIds: string[] ): Promise> => { const { body: updatedAlert } = await supertest @@ -70,7 +70,7 @@ export const getAlertById = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; id: string; index: string; expectedHttpCode?: number; @@ -97,7 +97,7 @@ export const createCaseAttachAlertAndDeleteAlert = async ({ alerts, getAlerts, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; totalCases: number; indexOfCaseToDelete: number; owner: string; @@ -145,7 +145,7 @@ export const createCaseAttachAlertAndDeleteCase = async ({ alerts, getAlerts, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; totalCases: number; indicesOfCaseToDelete: number[]; owner: string; @@ -194,7 +194,7 @@ export const createCaseAndAttachAlert = async ({ alerts, getAlerts, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; totalCases: number; owner: string; alerts: Alerts; diff --git a/x-pack/test/cases_api_integration/common/lib/api/attachments.ts b/x-pack/test/cases_api_integration/common/lib/api/attachments.ts index 1dbee9fbd4616..2f598f5a23b11 100644 --- a/x-pack/test/cases_api_integration/common/lib/api/attachments.ts +++ b/x-pack/test/cases_api_integration/common/lib/api/attachments.ts @@ -33,7 +33,7 @@ export const bulkGetAttachments = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; attachmentIds: string[]; caseId: string; auth?: { user: User; space: string | null }; @@ -57,12 +57,12 @@ export const createComment = async ({ expectedHttpCode = 200, headers = {}, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; params: AttachmentRequest; auth?: { user: User; space: string | null } | null; expectedHttpCode?: number; - headers?: Record; + headers?: Record; }): Promise => { const apiCall = supertest.post( `${getSpaceUrlPrefix(auth?.space)}${CASES_URL}/${caseId}/comments` @@ -86,7 +86,7 @@ export const bulkCreateAttachments = async ({ auth = { user: superUser, space: null }, expectedHttpCode = 200, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; params: BulkCreateAttachmentsRequest; auth?: { user: User; space: string | null }; @@ -110,7 +110,7 @@ export const createCaseAndBulkCreateAttachments = async ({ auth = { user: superUser, space: null }, expectedHttpCode = 200, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; numberOfAttachments?: number; auth?: { user: User; space: string | null }; expectedHttpCode?: number; @@ -156,7 +156,7 @@ export const deleteComment = async ({ expectedHttpCode = 204, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; commentId: string; expectedHttpCode?: number; @@ -178,7 +178,7 @@ export const deleteAllComments = async ({ expectedHttpCode = 204, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -199,7 +199,7 @@ export const getAllComments = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; auth?: { user: User; space: string | null }; expectedHttpCode?: number; @@ -219,7 +219,7 @@ export const getComment = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; commentId: string; expectedHttpCode?: number; @@ -241,12 +241,12 @@ export const updateComment = async ({ auth = { user: superUser, space: null }, headers = {}, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; req: AttachmentPatchRequest; expectedHttpCode?: number; auth?: { user: User; space: string | null } | null; - headers?: Record; + headers?: Record; }): Promise => { const apiCall = supertest.patch( `${getSpaceUrlPrefix(auth?.space)}${CASES_URL}/${caseId}/comments` @@ -269,7 +269,7 @@ export const bulkDeleteFileAttachments = async ({ expectedHttpCode = 204, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; fileIds: string[]; expectedHttpCode?: number; @@ -290,7 +290,7 @@ export const findAttachments = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; query?: Record; expectedHttpCode?: number; diff --git a/x-pack/test/cases_api_integration/common/lib/api/case.ts b/x-pack/test/cases_api_integration/common/lib/api/case.ts index a6605d8e83aab..dd1b668b429cf 100644 --- a/x-pack/test/cases_api_integration/common/lib/api/case.ts +++ b/x-pack/test/cases_api_integration/common/lib/api/case.ts @@ -15,11 +15,11 @@ import { superUser } from '../authentication/users'; import { getSpaceUrlPrefix, setupAuth } from './helpers'; export const createCase = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, params: CasePostRequest, expectedHttpCode: number = 200, auth: { user: User; space: string | null } | null = { user: superUser, space: null }, - headers: Record = {} + headers: Record = {} ): Promise => { const apiCall = supertest.post(`${getSpaceUrlPrefix(auth?.space)}${CASES_URL}`); @@ -44,7 +44,7 @@ export const deleteCases = async ({ expectedHttpCode = 204, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseIDs: string[]; expectedHttpCode?: number; auth?: { user: User; space: string | null }; diff --git a/x-pack/test/cases_api_integration/common/lib/api/configuration.ts b/x-pack/test/cases_api_integration/common/lib/api/configuration.ts index cfaa18b430c11..c74c6be78da82 100644 --- a/x-pack/test/cases_api_integration/common/lib/api/configuration.ts +++ b/x-pack/test/cases_api_integration/common/lib/api/configuration.ts @@ -60,11 +60,11 @@ export const getConfigurationOutput = (update = false, overwrite = {}): Partial< }; export const createConfiguration = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, req: ConfigurationRequest = getConfigurationRequest(), expectedHttpCode: number = 200, auth: { user: User; space: string | null } | null = { user: superUser, space: null }, - headers: Record = {} + headers: Record = {} ): Promise => { const apiCall = supertest.post(`${getSpaceUrlPrefix(auth?.space)}${CASE_CONFIGURE_URL}`); @@ -86,7 +86,7 @@ export const getConfiguration = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; query?: Record; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -102,12 +102,12 @@ export const getConfiguration = async ({ }; export const updateConfiguration = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, id: string, req: ConfigurationPatchRequest, expectedHttpCode: number = 200, auth: { user: User; space: string | null } | null = { user: superUser, space: null }, - headers: Record = {} + headers: Record = {} ): Promise => { const apiCall = supertest.patch(`${getSpaceUrlPrefix(auth?.space)}${CASE_CONFIGURE_URL}/${id}`); diff --git a/x-pack/test/cases_api_integration/common/lib/api/connectors.ts b/x-pack/test/cases_api_integration/common/lib/api/connectors.ts index 0eb6854cb735d..3bd959b031ae5 100644 --- a/x-pack/test/cases_api_integration/common/lib/api/connectors.ts +++ b/x-pack/test/cases_api_integration/common/lib/api/connectors.ts @@ -194,7 +194,7 @@ export const getCaseConnectors = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; expectedHttpCode?: number; auth?: { user: User; space: string | null }; }): Promise => { @@ -215,13 +215,13 @@ export const createCaseWithConnector = async ({ createCaseReq = getPostCaseRequest(), headers = {}, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; serviceNowSimulatorURL: string; actionsRemover: ActionsRemover; configureReq?: Record; auth?: { user: User; space: string | null } | null; createCaseReq?: CasePostRequest; - headers?: Record; + headers?: Record; }): Promise<{ postedCase: Case; connector: CreateConnectorResponse; @@ -288,7 +288,7 @@ export const createConnector = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; req: Record; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -309,7 +309,7 @@ export const getConnectors = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -329,7 +329,7 @@ export const executeConnector = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; connectorId: string; req: Record; expectedHttpCode?: number; @@ -352,7 +352,7 @@ export const executeSystemConnector = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; connectorId: string; req: Record; expectedHttpCode?: number; diff --git a/x-pack/test/cases_api_integration/common/lib/api/files.ts b/x-pack/test/cases_api_integration/common/lib/api/files.ts index 71951564c6a28..8e2710603020f 100644 --- a/x-pack/test/cases_api_integration/common/lib/api/files.ts +++ b/x-pack/test/cases_api_integration/common/lib/api/files.ts @@ -23,7 +23,7 @@ export const downloadFile = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; fileId: string; kind: string; mimeType: string; @@ -49,7 +49,7 @@ export const deleteFileForFileKind = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; fileKind: string; id: string; expectedHttpCode?: number; @@ -72,7 +72,7 @@ export const deleteFiles = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; files: string[]; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -91,7 +91,7 @@ export const deleteAllFilesForKind = async ({ auth = { user: superUser, space: null }, expectedHttpCode = 200, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; kind: string; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -124,7 +124,7 @@ export const deleteAllFiles = async ({ expectedHttpCode = 200, ignoreErrors = true, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; expectedHttpCode?: number; auth?: { user: User; space: string | null }; ignoreErrors?: boolean; @@ -165,7 +165,7 @@ export const getFileById = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; id: string; kind: string; expectedHttpCode?: number; @@ -185,7 +185,7 @@ export const listFiles = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; params: Parameters[0]; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -213,7 +213,7 @@ export const createFile = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; params: CreateFileSchema; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -238,7 +238,7 @@ export const uploadFile = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; data: string | object; kind: string; fileId: string; @@ -264,7 +264,7 @@ export const createAndUploadFile = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; data: string | object; createFileParams: CreateFileSchema; expectedHttpCode?: number; diff --git a/x-pack/test/cases_api_integration/common/lib/api/index.ts b/x-pack/test/cases_api_integration/common/lib/api/index.ts index 0b7f9c542a6d0..6582601f0f1a9 100644 --- a/x-pack/test/cases_api_integration/common/lib/api/index.ts +++ b/x-pack/test/cases_api_integration/common/lib/api/index.ts @@ -129,7 +129,7 @@ export const setStatus = async ({ supertest, cases, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; cases: SetStatusCasesParams[]; }): Promise => { const { body }: { body: Cases } = await supertest @@ -143,7 +143,7 @@ export const setStatus = async ({ /** * Add case as a connector */ -export const createCaseAction = async (supertest: SuperTest.SuperTest) => { +export const createCaseAction = async (supertest: SuperTest.Agent) => { const { body: createdAction } = await supertest .post('/api/actions/connector') .set('kbn-xsrf', 'foo') @@ -159,10 +159,7 @@ export const createCaseAction = async (supertest: SuperTest.SuperTest, - id: string -) => { +export const deleteCaseAction = async (supertest: SuperTest.Agent, id: string) => { await supertest.delete(`/api/actions/connector/${id}`).set('kbn-xsrf', 'foo'); }; @@ -418,11 +415,11 @@ export const updateCase = async ({ auth = { user: superUser, space: null }, headers = {}, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; params: CasesPatchRequest; expectedHttpCode?: number; auth?: { user: User; space: string | null } | null; - headers?: Record; + headers?: Record; }): Promise => { const apiCall = supertest.patch(`${getSpaceUrlPrefix(auth?.space)}${CASES_URL}`); @@ -444,7 +441,7 @@ export const getAllCasesStatuses = async ({ auth = { user: superUser, space: null }, query = {}, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; expectedHttpCode?: number; auth?: { user: User; space: string | null }; query?: Record; @@ -465,7 +462,7 @@ export const getCase = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; includeComments?: boolean; expectedHttpCode?: number; @@ -490,7 +487,7 @@ export const getCaseMetrics = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; features: CaseMetricsFeature[] | CaseMetricsFeature; expectedHttpCode?: number; @@ -512,7 +509,7 @@ export const resolveCase = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; includeComments?: boolean; expectedHttpCode?: number; @@ -537,7 +534,7 @@ export const findCases = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; query?: Record; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -560,7 +557,7 @@ export const getCasesByAlert = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; alertID: string; query?: Record; expectedHttpCode?: number; @@ -581,7 +578,7 @@ export const getTags = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; query?: Record; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -602,7 +599,7 @@ export const getReporters = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; query?: Record; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -623,7 +620,7 @@ export const getCategories = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; query?: Record; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -646,12 +643,12 @@ export const pushCase = async ({ auth = { user: superUser, space: null }, headers = {}, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; connectorId: string; expectedHttpCode?: number; auth?: { user: User; space: string | null } | null; - headers?: Record; + headers?: Record; }): Promise => { const apiCall = supertest.post( `${getSpaceUrlPrefix(auth?.space)}${CASES_URL}/${caseId}/connector/${connectorId}/_push` @@ -674,7 +671,7 @@ export const getAlertsAttachedToCase = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -727,7 +724,7 @@ export const getCasesMetrics = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; features: string[] | string; query?: Record; expectedHttpCode?: number; @@ -774,7 +771,7 @@ export const bulkGetCases = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; ids: string[]; fields?: string[]; expectedHttpCode?: number; @@ -796,7 +793,7 @@ export const searchCases = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; body?: Record; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -820,13 +817,13 @@ export const replaceCustomField = async ({ auth = { user: superUser, space: null }, headers = {}, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; customFieldId: string; params: CustomFieldPutRequest; expectedHttpCode?: number; auth?: { user: User; space: string | null } | null; - headers?: Record; + headers?: Record; }): Promise => { const apiCall = supertest.put( `${getSpaceUrlPrefix( diff --git a/x-pack/test/cases_api_integration/common/lib/api/user_actions.ts b/x-pack/test/cases_api_integration/common/lib/api/user_actions.ts index fc4f8382d3508..4a0f38b5dcc90 100644 --- a/x-pack/test/cases_api_integration/common/lib/api/user_actions.ts +++ b/x-pack/test/cases_api_integration/common/lib/api/user_actions.ts @@ -42,7 +42,7 @@ export const getCaseUserActions = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseID: string; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -61,7 +61,7 @@ export const findCaseUserActions = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseID: string; options?: UserActionFindRequest; expectedHttpCode?: number; @@ -82,7 +82,7 @@ export const getCaseUserActionStats = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseID: string; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -101,7 +101,7 @@ export const getCaseUsers = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; expectedHttpCode?: number; auth?: { user: User; space: string | null }; diff --git a/x-pack/test/cases_api_integration/common/lib/api/user_profiles.ts b/x-pack/test/cases_api_integration/common/lib/api/user_profiles.ts index bc78feab4070c..f9a66fe0f7962 100644 --- a/x-pack/test/cases_api_integration/common/lib/api/user_profiles.ts +++ b/x-pack/test/cases_api_integration/common/lib/api/user_profiles.ts @@ -38,7 +38,7 @@ export const bulkGetUserProfiles = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; req: BulkGetUserProfilesParams; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -62,7 +62,7 @@ export const suggestUserProfiles = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; req: SuggestUserProfilesRequest; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -89,10 +89,10 @@ export const updateUserProfileAvatar = async ({ expectedHttpCode = 200, headers = {}, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; req: UserProfileAvatarData; expectedHttpCode?: number; - headers?: Record; + headers?: Record; }): Promise => { await supertest .post('/internal/security/user_profile/_data') @@ -106,7 +106,7 @@ export const loginUsers = async ({ supertest, users = [superUser], }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; users?: User[]; }) => { const cookies: Cookie[] = []; diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/bulk_create_cases.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/bulk_create_cases.ts index 8177649f22ab3..c07d858e4b389 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/bulk_create_cases.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/bulk_create_cases.ts @@ -52,7 +52,7 @@ export default ({ getService }: FtrProviderContext): void => { expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - superTestService?: SuperTest.SuperTest; + superTestService?: SuperTest.Agent; data: object; expectedHttpCode?: number; auth?: { user: User; space: string | null }; 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 4c3239fe0d126..ef0e09f1a477d 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 @@ -700,7 +700,7 @@ const createCaseWithFiles = async ({ owner, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; fileKind: string; owner: string; auth?: { user: User; space: string | null }; diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/import_export.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/import_export.ts index 060b6463c0451..3aa11c83947ad 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/import_export.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/import_export.ts @@ -161,7 +161,7 @@ export default ({ getService }: FtrProviderContext): void => { }); }; -const expectImportToHaveOneCase = async (supertestService: supertest.SuperTest) => { +const expectImportToHaveOneCase = async (supertestService: supertest.Agent) => { const findResponse = await findCases({ supertest: supertestService, query: {} }); expect(findResponse.total).to.eql(1); expect(findResponse.cases[0].title).to.eql('A case with a connector'); diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/connectors/cases/cases_connector.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/connectors/cases/cases_connector.ts index b0fcd30fa5a42..060222473c15b 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/connectors/cases/cases_connector.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/connectors/cases/cases_connector.ts @@ -1352,7 +1352,7 @@ const executeConnectorAndVerifyCorrectness = async ({ connectorId, req, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; connectorId: string; req: Record; }) => { diff --git a/x-pack/test/common/services/bsearch_secure.ts b/x-pack/test/common/services/bsearch_secure.ts index 0ab967462767f..01050d1bb60c0 100644 --- a/x-pack/test/common/services/bsearch_secure.ts +++ b/x-pack/test/common/services/bsearch_secure.ts @@ -28,7 +28,7 @@ const getSpaceUrlPrefix = (spaceId?: string): string => { }; interface SendOptions { - supertestWithoutAuth: SuperTest.SuperTest; + supertestWithoutAuth: SuperTest.Agent; auth: { username: string; password: string }; referer?: string; kibanaVersion?: string; diff --git a/x-pack/test/common/utils/security_solution/detections_response/alerts/create_alerts_index.ts b/x-pack/test/common/utils/security_solution/detections_response/alerts/create_alerts_index.ts index 932db176942d9..da7f7b2cf36a6 100644 --- a/x-pack/test/common/utils/security_solution/detections_response/alerts/create_alerts_index.ts +++ b/x-pack/test/common/utils/security_solution/detections_response/alerts/create_alerts_index.ts @@ -17,7 +17,7 @@ import { countDownTest } from '../count_down_test'; * @param supertest The supertest client library */ export const createAlertsIndex = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog ): Promise => { await countDownTest( diff --git a/x-pack/test/common/utils/security_solution/detections_response/alerts/delete_all_alerts.ts b/x-pack/test/common/utils/security_solution/detections_response/alerts/delete_all_alerts.ts index e15aa4453282a..f29237b237694 100644 --- a/x-pack/test/common/utils/security_solution/detections_response/alerts/delete_all_alerts.ts +++ b/x-pack/test/common/utils/security_solution/detections_response/alerts/delete_all_alerts.ts @@ -16,7 +16,7 @@ import { countDownTest } from '../count_down_test'; * For use inside of afterEach blocks of tests */ export const deleteAllAlerts = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, es: Client, index: Array<'.alerts-security.alerts-*' | '.preview.alerts-security.alerts-*'> = [ diff --git a/x-pack/test/common/utils/security_solution/detections_response/alerts/get_alerts_by_id.ts b/x-pack/test/common/utils/security_solution/detections_response/alerts/get_alerts_by_id.ts index 1a3f7e29c26c6..92791edd5ea7e 100644 --- a/x-pack/test/common/utils/security_solution/detections_response/alerts/get_alerts_by_id.ts +++ b/x-pack/test/common/utils/security_solution/detections_response/alerts/get_alerts_by_id.ts @@ -20,7 +20,7 @@ import { getQueryAlertsId } from './get_query_alerts_ids'; * @param ids Rule id */ export const getAlertsById = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, id: string ): Promise> => { diff --git a/x-pack/test/common/utils/security_solution/detections_response/alerts/get_alerts_by_ids.ts b/x-pack/test/common/utils/security_solution/detections_response/alerts/get_alerts_by_ids.ts index 1cfc922d76677..d090a39ff3779 100644 --- a/x-pack/test/common/utils/security_solution/detections_response/alerts/get_alerts_by_ids.ts +++ b/x-pack/test/common/utils/security_solution/detections_response/alerts/get_alerts_by_ids.ts @@ -23,7 +23,7 @@ import { routeWithNamespace } from '../route_with_namespace'; * @param ids Array of the rule ids */ export const getAlertsByIds = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, ids: string[], size?: number, diff --git a/x-pack/test/common/utils/security_solution/detections_response/alerts/wait_for_alerts_to_be_present.ts b/x-pack/test/common/utils/security_solution/detections_response/alerts/wait_for_alerts_to_be_present.ts index f7e873f98d4c7..5772861f6e636 100644 --- a/x-pack/test/common/utils/security_solution/detections_response/alerts/wait_for_alerts_to_be_present.ts +++ b/x-pack/test/common/utils/security_solution/detections_response/alerts/wait_for_alerts_to_be_present.ts @@ -18,7 +18,7 @@ import { waitFor } from '../wait_for'; * @param numberOfAlerts The number of alerts to wait for, default is 1 */ export const waitForAlertsToBePresent = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, numberOfAlerts = 1, alertIds: string[], diff --git a/x-pack/test/common/utils/security_solution/detections_response/rules/create_rule.ts b/x-pack/test/common/utils/security_solution/detections_response/rules/create_rule.ts index ac6e44b2aab83..b1fe58770abfd 100644 --- a/x-pack/test/common/utils/security_solution/detections_response/rules/create_rule.ts +++ b/x-pack/test/common/utils/security_solution/detections_response/rules/create_rule.ts @@ -27,7 +27,7 @@ import { routeWithNamespace } from '../route_with_namespace'; * @param rule The rule to create */ export const createRule = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, rule: RuleCreateProps, namespace?: string diff --git a/x-pack/test/common/utils/security_solution/detections_response/rules/delete_all_rules.ts b/x-pack/test/common/utils/security_solution/detections_response/rules/delete_all_rules.ts index 8fe87827e4e25..66bf65ccfbc20 100644 --- a/x-pack/test/common/utils/security_solution/detections_response/rules/delete_all_rules.ts +++ b/x-pack/test/common/utils/security_solution/detections_response/rules/delete_all_rules.ts @@ -19,7 +19,7 @@ import { countDownTest } from '../count_down_test'; * @param supertest The supertest agent. */ export const deleteAllRules = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog ): Promise => { await countDownTest( diff --git a/x-pack/test/common/utils/security_solution/detections_response/rules/delete_rule.ts b/x-pack/test/common/utils/security_solution/detections_response/rules/delete_rule.ts index f4eff397aba0b..5b3e28f05e093 100644 --- a/x-pack/test/common/utils/security_solution/detections_response/rules/delete_rule.ts +++ b/x-pack/test/common/utils/security_solution/detections_response/rules/delete_rule.ts @@ -17,7 +17,7 @@ import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common * @param ruleId The rule id to delete */ export const deleteRule = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, ruleId: string ): Promise => { const response = await supertest diff --git a/x-pack/test/common/utils/security_solution/detections_response/rules/wait_for_rule_status.ts b/x-pack/test/common/utils/security_solution/detections_response/rules/wait_for_rule_status.ts index af0838b29613d..975b8dbef3b72 100644 --- a/x-pack/test/common/utils/security_solution/detections_response/rules/wait_for_rule_status.ts +++ b/x-pack/test/common/utils/security_solution/detections_response/rules/wait_for_rule_status.ts @@ -16,7 +16,7 @@ import { waitFor } from '../wait_for'; import { routeWithNamespace } from '../route_with_namespace'; interface WaitForRuleStatusBaseParams { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; log: ToolingLog; afterDate?: Date; namespace?: string; diff --git a/x-pack/test/dataset_quality_api_integration/common/dataset_quality_api_supertest.ts b/x-pack/test/dataset_quality_api_integration/common/dataset_quality_api_supertest.ts index 79818e970a2ab..66823c794b017 100644 --- a/x-pack/test/dataset_quality_api_integration/common/dataset_quality_api_supertest.ts +++ b/x-pack/test/dataset_quality_api_integration/common/dataset_quality_api_supertest.ts @@ -12,7 +12,7 @@ import type { APIEndpoint } from '@kbn/dataset-quality-plugin/server/routes'; import { formatRequest } from '@kbn/server-route-repository'; import type { APIClientRequestParamsOf, APIReturnType } from '@kbn/dataset-quality-plugin/common'; -export function createDatasetQualityApiClient(st: supertest.SuperTest) { +export function createDatasetQualityApiClient(st: supertest.Agent) { return async ( options: { type?: 'form-data'; diff --git a/x-pack/test/dataset_quality_api_integration/tests/integrations/package_utils.ts b/x-pack/test/dataset_quality_api_integration/tests/integrations/package_utils.ts index 4aa403ee6d62a..ae2b9a01fa475 100644 --- a/x-pack/test/dataset_quality_api_integration/tests/integrations/package_utils.ts +++ b/x-pack/test/dataset_quality_api_integration/tests/integrations/package_utils.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SuperTest, Test } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; export interface IntegrationPackage { name: string; @@ -26,7 +26,7 @@ export async function installCustomIntegration({ supertest, customIntegration, }: { - supertest: SuperTest; + supertest: SuperTestAgent; customIntegration: CustomIntegration; }) { const { integrationName, datasets } = customIntegration; @@ -41,7 +41,7 @@ export async function installPackage({ supertest, pkg, }: { - supertest: SuperTest; + supertest: SuperTestAgent; pkg: IntegrationPackage; }) { const { name, version } = pkg; @@ -56,7 +56,7 @@ export async function uninstallPackage({ supertest, pkg, }: { - supertest: SuperTest; + supertest: SuperTestAgent; pkg: IntegrationPackage; }) { const { name, version } = pkg; diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_by_upload.ts b/x-pack/test/fleet_api_integration/apis/epm/install_by_upload.ts index f086ad8785d8d..6fe31c67c65a2 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/install_by_upload.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/install_by_upload.ts @@ -9,6 +9,7 @@ import fs from 'fs'; import path from 'path'; import expect from '@kbn/expect'; import { INGEST_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; +import { HTTPError } from 'superagent'; import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; import { skipIfNoDockerRegistry } from '../../helpers'; @@ -167,7 +168,7 @@ export default function (providerContext: FtrProviderContext) { .type('application/gzip') .send(buf) .expect(400); - expect(res.error.text).to.equal( + expect((res.error as HTTPError).text).to.equal( '{"statusCode":400,"error":"Bad Request","message":"Archive seems empty. Assumed content type was application/gzip, check if this matches the archive type."}' ); }); @@ -180,7 +181,7 @@ export default function (providerContext: FtrProviderContext) { .type('application/zip') .send(buf) .expect(400); - expect(res.error.text).to.equal( + expect((res.error as HTTPError).text).to.equal( '{"statusCode":400,"error":"Bad Request","message":"Error during extraction of package: Error: end of central directory record signature not found. Assumed content type was application/zip, check if this matches the archive type."}' ); }); @@ -193,7 +194,7 @@ export default function (providerContext: FtrProviderContext) { .type('application/zip') .send(buf) .expect(400); - expect(res.error.text).to.equal( + expect((res.error as HTTPError).text).to.equal( '{"statusCode":400,"error":"Bad Request","message":"Package contains more than one top-level directory; top-level directory found: apache-0.1.4; filePath: apache-0.1.3/manifest.yml"}' ); }); @@ -206,7 +207,7 @@ export default function (providerContext: FtrProviderContext) { .type('application/zip') .send(buf) .expect(400); - expect(res.error.text).to.equal( + expect((res.error as HTTPError).text).to.equal( '{"statusCode":400,"error":"Bad Request","message":"Manifest file apache-0.1.4/manifest.yml not found in paths."}' ); }); @@ -219,7 +220,7 @@ export default function (providerContext: FtrProviderContext) { .type('application/zip') .send(buf) .expect(400); - expect(res.error.text).to.equal( + expect((res.error as HTTPError).text).to.equal( '{"statusCode":400,"error":"Bad Request","message":"Could not parse top-level package manifest at top-level directory apache-0.1.4: YAMLException: bad indentation of a mapping entry at line 2, column 7:\\n name: apache\\n ^."}' ); }); @@ -232,7 +233,7 @@ export default function (providerContext: FtrProviderContext) { .type('application/zip') .send(buf) .expect(400); - expect(res.error.text).to.equal( + expect((res.error as HTTPError).text).to.equal( '{"statusCode":400,"error":"Bad Request","message":"Invalid top-level package manifest at top-level directory apache-0.1.4 (package name: apache): one or more fields missing of name, version, description, title, format_version, owner."}' ); }); @@ -245,7 +246,7 @@ export default function (providerContext: FtrProviderContext) { .type('application/zip') .send(buf) .expect(400); - expect(res.error.text).to.equal( + expect((res.error as HTTPError).text).to.equal( '{"statusCode":400,"error":"Bad Request","message":"Name thisIsATypo and version 0.1.4 do not match top-level directory apache-0.1.4"}' ); }); diff --git a/x-pack/test/fleet_api_integration/helpers.ts b/x-pack/test/fleet_api_integration/helpers.ts index bf8729dba368b..2d9febe190892 100644 --- a/x-pack/test/fleet_api_integration/helpers.ts +++ b/x-pack/test/fleet_api_integration/helpers.ts @@ -15,7 +15,7 @@ import { } from '@kbn/fleet-plugin/common'; import { KbnClient } from '@kbn/test'; import { UNINSTALL_TOKENS_SAVED_OBJECT_TYPE } from '@kbn/fleet-plugin/common'; -import { SuperTest, Test } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { FtrProviderContext } from '../api_integration/ftr_provider_context'; export function warnAndSkipTest(mochaContext: Mocha.Context, log: ToolingLog) { @@ -109,7 +109,7 @@ export async function generateAgent( }); } -export function setPrereleaseSetting(supertest: SuperTest) { +export function setPrereleaseSetting(supertest: SuperTestAgent) { before(async () => { await supertest .put('/api/fleet/settings') @@ -126,7 +126,7 @@ export function setPrereleaseSetting(supertest: SuperTest) { } export const generateNAgentPolicies = async ( - supertest: SuperTest, + supertest: SuperTestAgent, number: number, overwrite?: Partial ): Promise => { @@ -142,7 +142,7 @@ export const generateNAgentPolicies = async ( }; export const generateAgentPolicy = async ( - supertest: SuperTest, + supertest: SuperTestAgent, overwrite?: Partial ): Promise => { const response = await supertest diff --git a/x-pack/test/functional/apps/dashboard/group1/created_by.ts b/x-pack/test/functional/apps/dashboard/group1/created_by.ts index 0d6eddf603d6e..00c280f28f96d 100644 --- a/x-pack/test/functional/apps/dashboard/group1/created_by.ts +++ b/x-pack/test/functional/apps/dashboard/group1/created_by.ts @@ -9,25 +9,69 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getPageObjects, getService }: FtrProviderContext) { - const PageObjects = getPageObjects(['common', 'dashboard', 'visualize', 'lens', 'timePicker']); + const PageObjects = getPageObjects([ + 'common', + 'dashboard', + 'visualize', + 'lens', + 'timePicker', + 'security', + ]); const esArchiver = getService('esArchiver'); const listingTable = getService('listingTable'); const kibanaServer = getService('kibanaServer'); - const config = getService('config'); + const security = getService('security'); + const testSubjects = getService('testSubjects'); describe('created_by', function () { const DASHBOARD_NAME = 'veryuniquemydashboardname'; - // this is the user that will be used to create the dashboard - const username = config.get('security.disableTestUser') - ? (config.get('servers.kibana.username') as string) - : 'test_user'; + const USERNAME_1 = 'global_dashboard_all_user_1'; + const USERNAME_2 = 'global_dashboard_all_user_2'; + before(async () => { await esArchiver.emptyKibanaIndex(); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); await kibanaServer.importExport.load( 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' ); + await kibanaServer.importExport.load( + 'x-pack/test/functional/fixtures/kbn_archiver/lens/lens_basic.json' + ); + + // ensure we're logged out so we can login as the appropriate users + await PageObjects.security.forceLogout(); + + await security.role.create('global_dashboard_all_role', { + elasticsearch: { + indices: [{ names: ['logstash-*'], privileges: ['read', 'view_index_metadata'] }], + }, + kibana: [ + { + feature: { + dashboard: ['all'], + visualize: ['all'], + }, + spaces: ['*'], + }, + ], + }); + + await security.user.create(USERNAME_1, { + password: 'changeme', + roles: ['global_dashboard_all_role'], + full_name: 'global dashboard all user 1', + }); + await security.user.create(USERNAME_2, { + password: 'changeme', + roles: ['global_dashboard_all_role'], + full_name: 'global dashboard all user 2', + }); + + await PageObjects.security.login(USERNAME_1, 'changeme', { + expectSpaceSelector: false, + }); + await PageObjects.dashboard.navigateToApp(); await PageObjects.dashboard.preserveCrossAppState(); await PageObjects.dashboard.clickNewDashboard(); @@ -39,25 +83,58 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); after(async () => { + // logout, so the other tests don't accidentally run as the custom users we're testing below + await PageObjects.security.forceLogout(); + await security.role.delete('global_dashboard_all_role'); + await security.user.delete(USERNAME_1); + await security.user.delete(USERNAME_2); + await esArchiver.unload('x-pack/test/functional/es_archives/logstash_functional'); await kibanaServer.importExport.unload( 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' ); + await kibanaServer.importExport.unload( + 'x-pack/test/functional/fixtures/kbn_archiver/lens/lens_basic.json' + ); + }); + + it('shows creator column', async () => { + await testSubjects.existOrFail('tableHeaderCell_createdBy_1'); + await testSubjects.existOrFail(`userAvatarTip-${USERNAME_1}`); }); it('can filter by created_by', async () => { await listingTable.expectItemsCount('dashboard', 20); - await listingTable.selectUsers(username); + await listingTable.selectUsers(USERNAME_1); await listingTable.expectItemsCount('dashboard', 1); expect(await listingTable.getAllItemsNames()).to.eql([DASHBOARD_NAME]); // unselect the user and select "no owner" option - await listingTable.selectUsers('null', username); + await listingTable.selectUsers('null', USERNAME_1); await listingTable.searchAndExpectItemsCount('dashboard', DASHBOARD_NAME, 0); // select the user and unselect "no owner" option - await listingTable.selectUsers('null', username); // select the user and unselect "no owner" option + await listingTable.selectUsers('null', USERNAME_1); // select the user and unselect "no owner" option await listingTable.expectItemsCount('dashboard', 1); }); + + it("doesn't override creator when editing a dashboard", async () => { + await PageObjects.security.forceLogout(); + await PageObjects.security.login(USERNAME_2, 'changeme', { + expectSpaceSelector: false, + }); + await PageObjects.dashboard.navigateToApp(); + await testSubjects.existOrFail('tableHeaderCell_createdBy_1'); + await testSubjects.existOrFail(`userAvatarTip-${USERNAME_1}`); + await PageObjects.dashboard.gotoDashboardEditMode(DASHBOARD_NAME); + await PageObjects.dashboard.addVisualizations(['A Pie']); + await PageObjects.dashboard.saveDashboard(DASHBOARD_NAME, { + waitDialogIsClosed: false, + exitFromEditMode: false, + }); + await PageObjects.dashboard.gotoDashboardLandingPage(); + await testSubjects.existOrFail('tableHeaderCell_createdBy_1'); + await testSubjects.missingOrFail(`userAvatarTip-${USERNAME_2}`); + }); }); } diff --git a/x-pack/test/functional/apps/dashboard/group3/reporting/screenshots.ts b/x-pack/test/functional/apps/dashboard/group3/reporting/screenshots.ts index 58e0495bb9f68..0031e1b845bcc 100644 --- a/x-pack/test/functional/apps/dashboard/group3/reporting/screenshots.ts +++ b/x-pack/test/functional/apps/dashboard/group3/reporting/screenshots.ts @@ -155,7 +155,8 @@ export default function ({ }); }); - describe('Preserve Layout', () => { + // FLAKY: https://github.com/elastic/kibana/issues/183566 + describe.skip('Preserve Layout', () => { before(async () => { await loadEcommerce(); }); diff --git a/x-pack/test/functional/apps/discover/visualize_field.ts b/x-pack/test/functional/apps/discover/visualize_field.ts index b1eadf89a5297..bb59bbc7af283 100644 --- a/x-pack/test/functional/apps/discover/visualize_field.ts +++ b/x-pack/test/functional/apps/discover/visualize_field.ts @@ -141,7 +141,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await dataViews.waitForSwitcherToBe('logst*'); }); - it('should visualize correctly text based language queries in Discover', async () => { + it('should visualize correctly ES|QL queries in Discover', async () => { await PageObjects.discover.selectTextBaseLang(); await PageObjects.header.waitUntilLoadingHasFinished(); await monacoEditor.setCodeEditorValue( @@ -182,7 +182,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { assertMatchesExpectedData(data!); }); - it('should visualize correctly text based language queries in Lens', async () => { + it('should visualize correctly ES|QL queries in Lens', async () => { await PageObjects.discover.selectTextBaseLang(); await PageObjects.header.waitUntilLoadingHasFinished(); await monacoEditor.setCodeEditorValue( @@ -201,7 +201,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); }); - it('should visualize correctly text based language queries based on index patterns', async () => { + it('should visualize correctly ES|QL queries based on index patterns', async () => { await PageObjects.discover.selectTextBaseLang(); await PageObjects.header.waitUntilLoadingHasFinished(); await monacoEditor.setCodeEditorValue( diff --git a/x-pack/test/functional/apps/search_playground/config.ts b/x-pack/test/functional/apps/search_playground/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/search_playground/config.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/search_playground/index.ts b/x-pack/test/functional/apps/search_playground/index.ts new file mode 100644 index 0000000000000..f15cbe9179868 --- /dev/null +++ b/x-pack/test/functional/apps/search_playground/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('playground', async () => { + loadTestFile(require.resolve('./playground_overview.ess.ts')); + }); +} diff --git a/x-pack/test/functional/apps/search_playground/playground_overview.ess.ts b/x-pack/test/functional/apps/search_playground/playground_overview.ess.ts new file mode 100644 index 0000000000000..48f6d5e13cf88 --- /dev/null +++ b/x-pack/test/functional/apps/search_playground/playground_overview.ess.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../ftr_provider_context'; +import { createOpenAIConnector } from './utils/create_openai_connector'; +import { MachineLearningCommonAPIProvider } from '../../services/ml/common_api'; + +const indexName = 'basic_index'; +const esArchiveIndex = 'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'; + +export default function (ftrContext: FtrProviderContext) { + const { getService, getPageObjects } = ftrContext; + const pageObjects = getPageObjects(['common', 'searchPlayground']); + const commonAPI = MachineLearningCommonAPIProvider(ftrContext); + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + const createIndex = async () => await esArchiver.load(esArchiveIndex); + let removeOpenAIConnector: () => Promise; + const createConnector = async () => { + removeOpenAIConnector = await createOpenAIConnector({ + supertest, + requestHeader: commonAPI.getCommonRequestHeader(), + }); + }; + + describe('Playground Overview', () => { + before(async () => { + await pageObjects.common.navigateToApp('enterpriseSearchApplications/playground'); + }); + + after(async () => { + await esArchiver.unload(esArchiveIndex); + await removeOpenAIConnector?.(); + }); + + describe('start chat page', () => { + it('playground app is loaded', async () => { + await pageObjects.searchPlayground.PlaygroundStartChatPage.expectPlaygroundStartChatPageComponentsToExist(); + await pageObjects.searchPlayground.PlaygroundStartChatPage.expectPlaygroundHeaderComponentsToExist(); + }); + + it('show no index callout', async () => { + await pageObjects.searchPlayground.PlaygroundStartChatPage.expectNoIndexCalloutExists(); + await pageObjects.searchPlayground.PlaygroundStartChatPage.expectCreateIndexButtonToExists(); + }); + + it('hide no index callout when index added', async () => { + await createIndex(); + await pageObjects.searchPlayground.PlaygroundStartChatPage.expectSelectIndex(indexName); + }); + + it('show add connector button', async () => { + await pageObjects.searchPlayground.PlaygroundStartChatPage.expectAddConnectorButtonExists(); + }); + + it('click add connector button opens connector flyout', async () => { + await pageObjects.searchPlayground.PlaygroundStartChatPage.expectOpenConnectorPagePlayground(); + }); + + it('hide gen ai panel when connector exists', async () => { + await pageObjects.searchPlayground.PlaygroundStartChatPage.expectHideGenAIPanelConnector( + createConnector + ); + }); + + it('show chat page', async () => { + await pageObjects.searchPlayground.PlaygroundStartChatPage.expectSelectIndex(indexName); + await pageObjects.searchPlayground.PlaygroundStartChatPage.expectToStartChatPage(); + }); + }); + + describe('chat page', () => { + it('chat works', async () => { + await pageObjects.searchPlayground.PlaygroundChatPage.expectChatWorks(); + }); + + it('open view code', async () => { + await pageObjects.searchPlayground.PlaygroundChatPage.expectOpenViewCode(); + }); + + it('show fields and code in view query', async () => { + await pageObjects.searchPlayground.PlaygroundChatPage.expectViewQueryHasFields(); + }); + + it('show edit context', async () => { + await pageObjects.searchPlayground.PlaygroundChatPage.expectEditContextOpens(); + }); + }); + }); +} diff --git a/x-pack/test/functional/apps/search_playground/utils/create_openai_connector.ts b/x-pack/test/functional/apps/search_playground/utils/create_openai_connector.ts new file mode 100644 index 0000000000000..864e424664785 --- /dev/null +++ b/x-pack/test/functional/apps/search_playground/utils/create_openai_connector.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type SuperTest from 'supertest'; + +export async function createOpenAIConnector({ + supertest, + requestHeader = {}, + apiKeyHeader = {}, +}: { + supertest: SuperTest.Agent; + requestHeader?: Record; + apiKeyHeader?: Record; +}): Promise<() => Promise> { + const config = { + apiProvider: 'OpenAI', + defaultModel: 'gpt-4', + apiUrl: 'http://localhost:3002', + }; + + const connector: { id: string } | undefined = ( + await supertest + .post('/api/actions/connector') + .set(requestHeader) + .set(apiKeyHeader) + .send({ + name: 'test Open AI', + connector_type_id: '.gen-ai', + config, + secrets: { + apiKey: 'genAiApiKey', + }, + }) + .expect(200) + ).body; + + return async () => { + if (connector) { + await supertest + .delete(`/api/actions/connector/${connector.id}`) + .set(requestHeader) + .set(apiKeyHeader) + .expect(204); + } + }; +} diff --git a/x-pack/test/functional/page_objects/index.ts b/x-pack/test/functional/page_objects/index.ts index 7a459ad74f980..155bc0901ae4d 100644 --- a/x-pack/test/functional/page_objects/index.ts +++ b/x-pack/test/functional/page_objects/index.ts @@ -53,6 +53,7 @@ import { UptimePageObject } from './uptime_page'; import { UserProfilePageProvider } from './user_profile_page'; import { WatcherPageObject } from './watcher_page'; import { SearchProfilerPageProvider } from './search_profiler_page'; +import { SearchPlaygroundPageProvider } from './search_playground_page'; // just like services, PageObjects are defined as a map of // names to Providers. Merge in Kibana's or pick specific ones @@ -93,6 +94,7 @@ export const pageObjects = { roleMappings: RoleMappingsPageProvider, rollup: RollupPageObject, searchProfiler: SearchProfilerPageProvider, + searchPlayground: SearchPlaygroundPageProvider, searchSessionsManagement: SearchSessionsPageProvider, security: SecurityPageObject, shareSavedObjectsToSpace: ShareSavedObjectsToSpacePageProvider, diff --git a/x-pack/test/functional/page_objects/index_management_page.ts b/x-pack/test/functional/page_objects/index_management_page.ts index 718dac14221ac..4cf5e6a36e376 100644 --- a/x-pack/test/functional/page_objects/index_management_page.ts +++ b/x-pack/test/functional/page_objects/index_management_page.ts @@ -129,6 +129,11 @@ export function IndexManagementPageProvider({ getService }: FtrProviderContext) return (await testSubjects.isDisplayed('indexDetailsHeader')) === true; }); }, + async expectIndexDetailsPageIsLoaded() { + await testSubjects.existOrFail('indexDetailsTab-overview'); + await testSubjects.existOrFail('indexDetailsContent'); + await testSubjects.existOrFail('indexDetailsBackToIndicesButton'); + }, }, async clickCreateIndexButton() { await testSubjects.click('createIndexButton'); @@ -153,5 +158,42 @@ export function IndexManagementPageProvider({ getService }: FtrProviderContext) ); expect(indexNames.some((i) => i === indexName)).to.be(true); }, + + async selectIndex(indexName: string) { + const id = `checkboxSelectIndex-${indexName}`; + const checkbox = await find.byCssSelector(`input[id="${id}"]`); + if (!(await checkbox.isSelected())) { + await find.clickByCssSelector(`input[id="${id}"]`); + } + }, + async clickManageButton() { + await testSubjects.existOrFail('indexActionsContextMenuButton'); + await testSubjects.click('indexActionsContextMenuButton'); + }, + async contextMenuIsVisible() { + await testSubjects.existOrFail('indexContextMenu'); + await testSubjects.existOrFail('deleteIndexMenuButton'); + await testSubjects.click('deleteIndexMenuButton'); + }, + async confirmDeleteModalIsVisible() { + await testSubjects.existOrFail('confirmModalTitleText'); + const modalText: string = await testSubjects.getVisibleText('confirmModalTitleText'); + expect(modalText).to.be('Delete index'); + await testSubjects.existOrFail('confirmModalConfirmButton'); + await testSubjects.click('confirmModalConfirmButton'); + // wait for index to be deleted + await testSubjects.missingOrFail('confirmModalConfirmButton'); + }, + + async expectIndexIsDeleted(indexName: string) { + const table = await find.byCssSelector('table'); + const rows = await table.findAllByTestSubject('indexTableRow'); + const indexNames: string[] = await Promise.all( + rows.map(async (row) => { + return await (await row.findByTestSubject('indexTableIndexNameLink')).getVisibleText(); + }) + ); + expect(indexNames.includes(indexName)).to.be(false); + }, }; } diff --git a/x-pack/test/functional/page_objects/search_playground_page.ts b/x-pack/test/functional/page_objects/search_playground_page.ts new file mode 100644 index 0000000000000..35e0a8cc253d3 --- /dev/null +++ b/x-pack/test/functional/page_objects/search_playground_page.ts @@ -0,0 +1,114 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../ftr_provider_context'; + +export function SearchPlaygroundPageProvider({ getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + const comboBox = getService('comboBox'); + const browser = getService('browser'); + + return { + PlaygroundStartChatPage: { + async expectPlaygroundStartChatPageComponentsToExist() { + await testSubjects.existOrFail('startChatPage'); + await testSubjects.existOrFail('selectIndicesChatPanel'); + await testSubjects.existOrFail('startChatButton'); + }, + + async expectPlaygroundHeaderComponentsToExist() { + await testSubjects.existOrFail('playground-header-actions'); + await testSubjects.existOrFail('playground-documentation-link'); + }, + + async expectCreateIndexButtonToMissed() { + await testSubjects.missingOrFail('createIndexButton'); + }, + + async expectCreateIndexButtonToExists() { + await testSubjects.existOrFail('createIndexButton'); + }, + + async expectNoIndexCalloutExists() { + await testSubjects.existOrFail('createIndexCallout'); + }, + + async expectSelectIndex(indexName: string) { + await browser.refresh(); + await testSubjects.missingOrFail('createIndexCallout'); + await testSubjects.existOrFail('selectIndicesComboBox'); + await comboBox.setCustom('selectIndicesComboBox', indexName); + }, + + async expectNoIndicesFieldsWarningExists() { + await testSubjects.existOrFail('NoIndicesFieldsMessage'); + }, + + async expectAddConnectorButtonExists() { + await testSubjects.existOrFail('setupGenAIConnectorButton'); + }, + + async expectOpenConnectorPagePlayground() { + await testSubjects.click('setupGenAIConnectorButton'); + await testSubjects.existOrFail('create-connector-flyout'); + }, + + async expectHideGenAIPanelConnector(createConnector: () => Promise) { + await createConnector(); + await browser.refresh(); + await testSubjects.missingOrFail('connectToLLMChatPanel'); + }, + + async expectToStartChatPage() { + expect(await testSubjects.isEnabled('startChatButton')).to.be(true); + await testSubjects.click('startChatButton'); + await testSubjects.existOrFail('chatPage'); + }, + }, + PlaygroundChatPage: { + async expectChatWorks() { + await testSubjects.existOrFail('questionInput'); + await testSubjects.setValue('questionInput', 'test question'); + await testSubjects.click('sendQuestionButton'); + await testSubjects.existOrFail('userMessage'); + }, + + async expectOpenViewCode() { + await testSubjects.click('viewCodeActionButton'); + await testSubjects.existOrFail('viewCodeFlyout'); + await testSubjects.click('euiFlyoutCloseButton'); + }, + + async expectViewQueryHasFields() { + await testSubjects.click('viewQueryActionButton'); + await testSubjects.existOrFail('viewQueryFlyout'); + const fields = await testSubjects.findAll('queryField'); + + expect(fields.length).to.be(1); + + const codeBlock = await testSubjects.find('ViewElasticsearchQueryResult'); + const code = await codeBlock.getVisibleText(); + expect(code.replace(/ /g, '')).to.be( + '{\n"retriever":{\n"standard":{\n"query":{\n"multi_match":{\n"query":"{query}",\n"fields":[\n"baz"\n]\n}\n}\n}\n}\n}' + ); + await testSubjects.click('euiFlyoutCloseButton'); + }, + + async expectEditContextOpens() { + await testSubjects.click('editContextActionButton'); + await testSubjects.existOrFail('editContextFlyout'); + await testSubjects.click('contextFieldsSelectable_basic_index'); + await testSubjects.existOrFail('contextField'); + const fields = await testSubjects.findAll('contextField'); + + expect(fields.length).to.be(1); + await testSubjects.click('euiFlyoutCloseButton'); + }, + }, + }; +} diff --git a/x-pack/test/functional/services/transform/api.ts b/x-pack/test/functional/services/transform/api.ts index 5aad29fff8942..15751c91253ec 100644 --- a/x-pack/test/functional/services/transform/api.ts +++ b/x-pack/test/functional/services/transform/api.ts @@ -237,7 +237,7 @@ export function TransformAPIProvider({ getService }: FtrProviderContext) { ); const { body, status } = await esSupertest .put(`/_transform/${transformId}${deferValidation ? '?defer_validation=true' : ''}`) - .set(headers) + .set(headers as Record) .send(transformConfig); this.assertResponseStatusCode(200, status, body); } else { diff --git a/x-pack/test/functional_with_es_ssl/apps/cases/group2/attachment_framework.ts b/x-pack/test/functional_with_es_ssl/apps/cases/group2/attachment_framework.ts index c45df81316e81..876e4d2123019 100644 --- a/x-pack/test/functional_with_es_ssl/apps/cases/group2/attachment_framework.ts +++ b/x-pack/test/functional_with_es_ssl/apps/cases/group2/attachment_framework.ts @@ -24,7 +24,7 @@ import { import { FtrProviderContext } from '../../../ftr_provider_context'; const createLogStashDataView = async ( - supertest: SuperTest.SuperTest + supertest: SuperTest.Agent ): Promise<{ data_view: { id: string } }> => { const { body } = await supertest .post(`/api/data_views/data_view`) @@ -36,7 +36,7 @@ const createLogStashDataView = async ( }; const deleteLogStashDataView = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, dataViewId: string ): Promise => { await supertest diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors/utils.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors/utils.ts index 09dffef846cd5..e96ec17eb9c2e 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors/utils.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors/utils.ts @@ -48,10 +48,7 @@ export const createSlackConnector = async ({ return connector; }; -export const getConnectorByName = async ( - name: string, - supertest: SuperTest.SuperTest -) => { +export const getConnectorByName = async (name: string, supertest: SuperTest.Agent) => { const { body } = await supertest .get(`/api/actions/connectors`) .set('kbn-xsrf', 'foo') @@ -65,7 +62,7 @@ export async function createRuleWithActionsAndParams( testRunUuid: string, params: Record = {}, overwrites: Record = {}, - supertest: SuperTest.SuperTest + supertest: SuperTest.Agent ) { return await createAlwaysFiringRule( { @@ -94,7 +91,7 @@ export async function createRuleWithActionsAndParams( async function createAlwaysFiringRule( overwrites: Record = {}, - supertest: SuperTest.SuperTest + supertest: SuperTest.Agent ) { const { body: createdRule } = await supertest .post(`/api/alerting/rule`) @@ -109,10 +106,7 @@ async function createAlwaysFiringRule( return createdRule; } -export async function getAlertSummary( - ruleId: string, - supertest: SuperTest.SuperTest -) { +export async function getAlertSummary(ruleId: string, supertest: SuperTest.Agent) { const { body: summary } = await supertest .get(`/internal/alerting/rule/${encodeURIComponent(ruleId)}/_alert_summary`) .expect(200); diff --git a/x-pack/test/monitoring_api_integration/packages.ts b/x-pack/test/monitoring_api_integration/packages.ts index 284bba089a55c..981845eb460dc 100644 --- a/x-pack/test/monitoring_api_integration/packages.ts +++ b/x-pack/test/monitoring_api_integration/packages.ts @@ -30,10 +30,7 @@ export const getPackagesArgs = (): string[] => { export const bundledPackagesLocation = path.join(path.dirname(__filename), '/fixtures/packages'); -export function installPackage( - supertest: SuperTest.SuperTest, - packageName: SupportedPackage -) { +export function installPackage(supertest: SuperTest.Agent, packageName: SupportedPackage) { const pkg = PACKAGES.find(({ name }) => name === packageName); const request = supertest .post('/api/fleet/epm/packages') diff --git a/x-pack/test/observability_ai_assistant_api_integration/common/observability_ai_assistant_api_client.ts b/x-pack/test/observability_ai_assistant_api_integration/common/observability_ai_assistant_api_client.ts index cb59adca9a86a..865620a2d028a 100644 --- a/x-pack/test/observability_ai_assistant_api_integration/common/observability_ai_assistant_api_client.ts +++ b/x-pack/test/observability_ai_assistant_api_integration/common/observability_ai_assistant_api_client.ts @@ -15,7 +15,7 @@ import supertest from 'supertest'; import { format } from 'url'; import { Subtract } from 'utility-types'; -export function createObservabilityAIAssistantApiClient(st: supertest.SuperTest) { +export function createObservabilityAIAssistantApiClient(st: supertest.Agent) { return ( options: { type?: 'form-data'; @@ -70,17 +70,62 @@ type WithoutPromise> = Subtract>; // end(one:string) // end(one:string, two:string) // } -// would lose the first signature. This keeps up to four signatures. +// would lose the first signature. This keeps up to eight signatures. type OverloadedParameters = T extends { (...args: infer A1): any; (...args: infer A2): any; (...args: infer A3): any; (...args: infer A4): any; + (...args: infer A5): any; + (...args: infer A6): any; + (...args: infer A7): any; + (...args: infer A8): any; } + ? A1 | A2 | A3 | A4 | A5 | A6 | A7 | A8 + : T extends { + (...args: infer A1): any; + (...args: infer A2): any; + (...args: infer A3): any; + (...args: infer A4): any; + (...args: infer A5): any; + (...args: infer A6): any; + (...args: infer A7): any; + } + ? A1 | A2 | A3 | A4 | A5 | A6 | A7 + : T extends { + (...args: infer A1): any; + (...args: infer A2): any; + (...args: infer A3): any; + (...args: infer A4): any; + (...args: infer A5): any; + (...args: infer A6): any; + } + ? A1 | A2 | A3 | A4 | A5 | A6 + : T extends { + (...args: infer A1): any; + (...args: infer A2): any; + (...args: infer A3): any; + (...args: infer A4): any; + (...args: infer A5): any; + } + ? A1 | A2 | A3 | A4 | A5 + : T extends { + (...args: infer A1): any; + (...args: infer A2): any; + (...args: infer A3): any; + (...args: infer A4): any; + } ? A1 | A2 | A3 | A4 - : T extends { (...args: infer A1): any; (...args: infer A2): any; (...args: infer A3): any } + : T extends { + (...args: infer A1): any; + (...args: infer A2): any; + (...args: infer A3): any; + } ? A1 | A2 | A3 - : T extends { (...args: infer A1): any; (...args: infer A2): any } + : T extends { + (...args: infer A1): any; + (...args: infer A2): any; + } ? A1 | A2 : T extends (...args: infer A) => any ? A diff --git a/x-pack/test/observability_ai_assistant_api_integration/tests/connectors/connectors.spec.ts b/x-pack/test/observability_ai_assistant_api_integration/tests/connectors/connectors.spec.ts index d5e726012c869..d51edffc9a1a8 100644 --- a/x-pack/test/observability_ai_assistant_api_integration/tests/connectors/connectors.spec.ts +++ b/x-pack/test/observability_ai_assistant_api_integration/tests/connectors/connectors.spec.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import type { SuperTest, Test } from 'supertest'; +import type { Agent as SuperTestAgent } from 'supertest'; import { FtrProviderContext } from '../../common/ftr_provider_context'; export default function ApiTest({ getService }: FtrProviderContext) { @@ -71,7 +71,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); } -export async function deleteAllActionConnectors(supertest: SuperTest): Promise { +export async function deleteAllActionConnectors(supertest: SuperTestAgent): Promise { const res = await supertest.get(`/api/actions/connectors`); const body = res.body as Array<{ id: string; connector_type_id: string; name: string }>; diff --git a/x-pack/test/observability_api_integration/common/obs_api_supertest.ts b/x-pack/test/observability_api_integration/common/obs_api_supertest.ts index e0788dcd6785d..69d7083f838e4 100644 --- a/x-pack/test/observability_api_integration/common/obs_api_supertest.ts +++ b/x-pack/test/observability_api_integration/common/obs_api_supertest.ts @@ -24,7 +24,7 @@ export type APIClientRequestParamsOf = ClientRequ TEndpoint >; -export function createObsApiClient(st: supertest.SuperTest) { +export function createObsApiClient(st: supertest.Agent) { return async ( options: { type?: 'form-data'; diff --git a/x-pack/test/observability_onboarding_api_integration/common/observability_onboarding_api_supertest.ts b/x-pack/test/observability_onboarding_api_integration/common/observability_onboarding_api_supertest.ts index c679c91e96525..199230e92c4ae 100644 --- a/x-pack/test/observability_onboarding_api_integration/common/observability_onboarding_api_supertest.ts +++ b/x-pack/test/observability_onboarding_api_integration/common/observability_onboarding_api_supertest.ts @@ -15,7 +15,7 @@ import type { import type { APIEndpoint } from '@kbn/observability-onboarding-plugin/server/routes'; import { formatRequest } from '@kbn/server-route-repository'; -export function createObservabilityOnboardingApiClient(st: supertest.SuperTest) { +export function createObservabilityOnboardingApiClient(st: supertest.Agent) { return async ( options: { type?: 'form-data'; diff --git a/x-pack/test/observability_onboarding_api_integration/configs/index.ts b/x-pack/test/observability_onboarding_api_integration/configs/index.ts index 8708ef8235d55..57e56a9b022a7 100644 --- a/x-pack/test/observability_onboarding_api_integration/configs/index.ts +++ b/x-pack/test/observability_onboarding_api_integration/configs/index.ts @@ -8,11 +8,11 @@ import { mapValues } from 'lodash'; import { createTestConfig, CreateTestConfig } from '../common/config'; -export const MOCKED_PUBLIC_BASE_URL = 'http://mockedPublicBaseUrl'; +export const MOCKED_PUBLIC_BASE_URL = 'http://mockedpublicbaseurl'; // my.mocked.domain$myMockedEsUr$myKibanaMockedUrl export const MOCKED_ENCODED_CLOUD_ID = 'bXkubW9ja2VkLmRvbWFpbiRteU1vY2tlZEVzVXJsJG15TW9ja2VkS2liYW5hVXJs'; -export const MOCKED_KIBANA_URL = 'https://myMockedKibanaUrl.my.mocked.domain:443'; +export const MOCKED_KIBANA_URL = 'https://mymockedkibanaurl.my.mocked.domain'; export const observabilityOnboardingDebugLogger = { name: 'plugins.observabilityOnboarding', diff --git a/x-pack/test/observability_onboarding_api_integration/tests/logs/environment.spec.ts b/x-pack/test/observability_onboarding_api_integration/tests/logs/environment.spec.ts index 41a14061b4059..2f7b514d54a57 100644 --- a/x-pack/test/observability_onboarding_api_integration/tests/logs/environment.spec.ts +++ b/x-pack/test/observability_onboarding_api_integration/tests/logs/environment.spec.ts @@ -29,7 +29,8 @@ export default function ApiTest({ getService }: FtrProviderContext) { ); expect(request.body.scriptDownloadUrl).to.match( new RegExp( - `${MOCKED_PUBLIC_BASE_URL}/.+?/plugins/observabilityOnboarding/assets/standalone_agent_setup.sh` + `${MOCKED_PUBLIC_BASE_URL}/.+?/plugins/observabilityOnboarding/assets/standalone_agent_setup.sh`, + 'i' ) ); }); @@ -45,7 +46,8 @@ export default function ApiTest({ getService }: FtrProviderContext) { ); expect(request.body.scriptDownloadUrl).to.match( new RegExp( - `${MOCKED_KIBANA_URL}/.+?/plugins/observabilityOnboarding/assets/standalone_agent_setup.sh` + `${MOCKED_KIBANA_URL}/.+?/plugins/observabilityOnboarding/assets/standalone_agent_setup.sh`, + 'i' ) ); }); diff --git a/x-pack/test/profiling_api_integration/common/api_supertest.ts b/x-pack/test/profiling_api_integration/common/api_supertest.ts index 11d7564248caf..dcd5c892d3b77 100644 --- a/x-pack/test/profiling_api_integration/common/api_supertest.ts +++ b/x-pack/test/profiling_api_integration/common/api_supertest.ts @@ -10,7 +10,7 @@ import request from 'superagent'; import supertest from 'supertest'; import { format } from 'url'; -export function createProfilingApiClient(st: supertest.SuperTest) { +export function createProfilingApiClient(st: supertest.Agent) { return async (options: { endpoint: string; params?: { diff --git a/x-pack/test/profiling_api_integration/common/bettertest.ts b/x-pack/test/profiling_api_integration/common/bettertest.ts index ca679a24539ac..ec2fd13763853 100644 --- a/x-pack/test/profiling_api_integration/common/bettertest.ts +++ b/x-pack/test/profiling_api_integration/common/bettertest.ts @@ -23,7 +23,7 @@ export type BetterTest = (options: { * This is useful for tests that expect a 200 response * It also makes it easier to debug tests that fail because of a 500 response. */ -export function getBettertest(st: supertest.SuperTest): BetterTest { +export function getBettertest(st: supertest.Agent): BetterTest { return async ({ pathname, method = 'get', query, body }) => { const url = format({ pathname, query }); @@ -60,7 +60,7 @@ export class BetterTestError extends Error { const req = res.req as any; super( `Unhandled BetterTestError: -Status: "${res.status}" +Status: "${res.status}" Path: "${req.method} ${req.path}" Body: ${JSON.stringify(res.body)}` ); diff --git a/x-pack/test/saved_object_api_integration/common/lib/create_users_and_roles.ts b/x-pack/test/saved_object_api_integration/common/lib/create_users_and_roles.ts index e0fed03efb94d..eb7a729ff5c96 100644 --- a/x-pack/test/saved_object_api_integration/common/lib/create_users_and_roles.ts +++ b/x-pack/test/saved_object_api_integration/common/lib/create_users_and_roles.ts @@ -5,11 +5,11 @@ * 2.0. */ -import type { SuperTest } from 'supertest'; +import type { Agent as SuperTestAgent } from 'supertest'; import type { Client } from '@elastic/elasticsearch'; import { AUTHENTICATION } from './authentication'; -export const createUsersAndRoles = async (es: Client, supertest: SuperTest) => { +export const createUsersAndRoles = async (es: Client, supertest: SuperTestAgent) => { await supertest .put('/api/security/role/kibana_legacy_user') .send({ diff --git a/x-pack/test/saved_object_api_integration/common/suites/bulk_create.ts b/x-pack/test/saved_object_api_integration/common/suites/bulk_create.ts index 38a4a8d8db8c7..6b79b34489111 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/bulk_create.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/bulk_create.ts @@ -258,7 +258,7 @@ export function bulkCreateTestSuiteFactory(context: FtrProviderContext) { const query = test.overwrite ? '?overwrite=true' : ''; await supertest .post(`${getUrlPrefix(spaceId)}/api/saved_objects/_bulk_create${query}`) - .auth(user?.username, user?.password) + .auth(user?.username!, user?.password!) .send(requestBody) .expect(test.responseStatusCode) .then(test.responseBody); diff --git a/x-pack/test/saved_object_api_integration/common/suites/bulk_delete.ts b/x-pack/test/saved_object_api_integration/common/suites/bulk_delete.ts index 578f8a4e0cd1f..f7fe4ba4061ce 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/bulk_delete.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/bulk_delete.ts @@ -138,7 +138,7 @@ export function bulkDeleteTestSuiteFactory(context: FtrProviderContext) { const query = testForce && testForce === true ? '?force=true' : ''; await supertest .post(`${getUrlPrefix(spaceId)}/api/saved_objects/_bulk_delete${query}`) - .auth(user?.username, user?.password) + .auth(user?.username!, user?.password!) .send(requestBody) .expect(test.responseStatusCode) .then(test.responseBody); diff --git a/x-pack/test/saved_object_api_integration/common/suites/bulk_get.ts b/x-pack/test/saved_object_api_integration/common/suites/bulk_get.ts index 15a51c3db3364..d25d1e0a5d87c 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/bulk_get.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/bulk_get.ts @@ -125,7 +125,7 @@ export function bulkGetTestSuiteFactory(context: FtrProviderContext) { it(`should return ${test.responseStatusCode} ${test.title}`, async () => { await supertest .post(`${getUrlPrefix(spaceId)}/api/saved_objects/_bulk_get`) - .auth(user?.username, user?.password) + .auth(user?.username!, user?.password!) .send(test.request) .expect(test.responseStatusCode) .then(test.responseBody); diff --git a/x-pack/test/saved_object_api_integration/common/suites/bulk_resolve.ts b/x-pack/test/saved_object_api_integration/common/suites/bulk_resolve.ts index a203865e294aa..b3c8669d48216 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/bulk_resolve.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/bulk_resolve.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { SuperTest } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { TEST_CASES } from './resolve'; import { SPACES } from '../lib/spaces'; import { @@ -29,7 +29,7 @@ export interface BulkResolveTestCase extends TestCase { export { TEST_CASES }; // re-export the (non-bulk) resolve test cases -export function bulkResolveTestSuiteFactory(esArchiver: any, supertest: SuperTest) { +export function bulkResolveTestSuiteFactory(esArchiver: any, supertest: SuperTestAgent) { const expectSavedObjectForbidden = expectResponses.forbiddenTypes('bulk_get'); const expectResponseBody = ( @@ -119,7 +119,7 @@ export function bulkResolveTestSuiteFactory(esArchiver: any, supertest: SuperTes it(`should return ${test.responseStatusCode} ${test.title}`, async () => { await supertest .post(`${getUrlPrefix(spaceId)}/api/saved_objects/_bulk_resolve`) - .auth(user?.username, user?.password) + .auth(user?.username!, user?.password!) .send(test.request) .expect(test.responseStatusCode) .then(test.responseBody); diff --git a/x-pack/test/saved_object_api_integration/common/suites/bulk_update.ts b/x-pack/test/saved_object_api_integration/common/suites/bulk_update.ts index e79089615e7ee..384b8db8aa0a7 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/bulk_update.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/bulk_update.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { SuperTest } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { SAVED_OBJECT_TEST_CASES as CASES } from '../lib/saved_object_test_cases'; import { SPACES } from '../lib/spaces'; import { expectResponses, getUrlPrefix, getTestTitle } from '../lib/saved_object_test_utils'; @@ -36,7 +36,7 @@ const createRequest = ({ type, id, namespace }: BulkUpdateTestCase) => ({ ...(namespace && { namespace }), // individual "object namespace" string }); -export function bulkUpdateTestSuiteFactory(esArchiver: any, supertest: SuperTest) { +export function bulkUpdateTestSuiteFactory(esArchiver: any, supertest: SuperTestAgent) { const expectSavedObjectForbidden = expectResponses.forbiddenTypes('bulk_update'); const expectResponseBody = ( @@ -118,7 +118,7 @@ export function bulkUpdateTestSuiteFactory(esArchiver: any, supertest: SuperTest const requestBody = test.request.map((x) => ({ ...x, ...attrs })); await supertest .put(`${getUrlPrefix(spaceId)}/api/saved_objects/_bulk_update`) - .auth(user?.username, user?.password) + .auth(user?.username!, user?.password!) .send(requestBody) .expect(test.responseStatusCode) .then(test.responseBody); diff --git a/x-pack/test/saved_object_api_integration/common/suites/create.ts b/x-pack/test/saved_object_api_integration/common/suites/create.ts index dfad5e638a708..b12fef1c7cade 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/create.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/create.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { SuperTest } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { SAVED_OBJECT_TEST_CASES as CASES } from '../lib/saved_object_test_cases'; import { SPACES, ALL_SPACES_ID } from '../lib/spaces'; import { @@ -85,7 +85,7 @@ const createRequest = ({ type, id, initialNamespaces }: CreateTestCase) => ({ initialNamespaces, }); -export function createTestSuiteFactory(esArchiver: any, supertest: SuperTest) { +export function createTestSuiteFactory(esArchiver: any, supertest: SuperTestAgent) { const expectSavedObjectForbidden = expectResponses.forbiddenTypes('create'); const expectResponseBody = (testCase: CreateTestCase, user?: TestUser): ExpectResponseBody => @@ -155,7 +155,7 @@ export function createTestSuiteFactory(esArchiver: any, supertest: SuperTest = Object.freeze({ */ const createRequest = ({ type, id, force }: DeleteTestCase) => ({ type, id, force }); -export function deleteTestSuiteFactory(es: Client, esArchiver: any, supertest: SuperTest) { +export function deleteTestSuiteFactory(es: Client, esArchiver: any, supertest: SuperTestAgent) { const expectSavedObjectForbidden = expectResponses.forbiddenTypes('delete'); const expectResponseBody = (testCase: DeleteTestCase): ExpectResponseBody => @@ -118,7 +118,7 @@ export function deleteTestSuiteFactory(es: Client, esArchiver: any, supertest: S await supertest .delete(`${getUrlPrefix(spaceId)}/api/saved_objects/${type}/${id}`) .query({ ...(force && { force }) }) - .auth(user?.username, user?.password) + .auth(user?.username!, user?.password!) .expect(test.responseStatusCode) .then(test.responseBody); }); diff --git a/x-pack/test/saved_object_api_integration/common/suites/export.ts b/x-pack/test/saved_object_api_integration/common/suites/export.ts index 6df4c2f8dd12c..b7936f94a98fb 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/export.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/export.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { SuperTest } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { SAVED_OBJECT_TEST_CASES, CONFLICT_TEST_CASES, @@ -154,7 +154,7 @@ const EMPTY_RESULT = { missingReferences: [], }; -export function exportTestSuiteFactory(esArchiver: any, supertest: SuperTest) { +export function exportTestSuiteFactory(esArchiver: any, supertest: SuperTestAgent) { const expectSavedObjectForbiddenBulkGet = expectResponses.forbiddenTypes('bulk_get'); const expectResponseBody = (testCase: ExportTestCase): ExpectResponseBody => @@ -259,7 +259,7 @@ export function exportTestSuiteFactory(esArchiver: any, supertest: SuperTest { await supertest .post(`${getUrlPrefix(spaceId)}/api/saved_objects/_export`) - .auth(user?.username, user?.password) + .auth(user?.username!, user?.password!) .send(test.request) .expect(test.responseStatusCode) .then(test.responseBody); diff --git a/x-pack/test/saved_object_api_integration/common/suites/find.ts b/x-pack/test/saved_object_api_integration/common/suites/find.ts index 202fc02dbfc1c..c7afc205f5b6f 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/find.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/find.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { SuperTest } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import querystring from 'querystring'; import { SAVED_OBJECT_TEST_CASES, @@ -187,7 +187,7 @@ export const createRequest = ({ query }: FindTestCase) => ({ query }); const getTestTitle = ({ failure, title }: FindTestCase) => `${failure?.reason || 'success'} ["${title}"]`; -export function findTestSuiteFactory(esArchiver: any, supertest: SuperTest) { +export function findTestSuiteFactory(esArchiver: any, supertest: SuperTestAgent) { const expectResponseBody = (testCase: FindTestCase, user?: TestUser): ExpectResponseBody => async (response: Record) => { @@ -298,7 +298,7 @@ export function findTestSuiteFactory(esArchiver: any, supertest: SuperTest) await supertest .get(`${getUrlPrefix(spaceId)}/api/saved_objects/_find${query}`) - .auth(user?.username, user?.password) + .auth(user?.username!, user?.password!) .expect(test.responseStatusCode) .then(test.responseBody); }); diff --git a/x-pack/test/saved_object_api_integration/common/suites/get.ts b/x-pack/test/saved_object_api_integration/common/suites/get.ts index b106930103f84..024f061d2cb23 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/get.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/get.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SuperTest } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { SAVED_OBJECT_TEST_CASES as CASES } from '../lib/saved_object_test_cases'; import { SPACES } from '../lib/spaces'; import { @@ -25,7 +25,7 @@ export type GetTestCase = TestCase; const DOES_NOT_EXIST = Object.freeze({ type: 'dashboard', id: 'does-not-exist' }); export const TEST_CASES: Record = Object.freeze({ ...CASES, DOES_NOT_EXIST }); -export function getTestSuiteFactory(esArchiver: any, supertest: SuperTest) { +export function getTestSuiteFactory(esArchiver: any, supertest: SuperTestAgent) { const expectSavedObjectForbidden = expectResponses.forbiddenTypes('get'); const expectResponseBody = (testCase: GetTestCase): ExpectResponseBody => @@ -81,7 +81,7 @@ export function getTestSuiteFactory(esArchiver: any, supertest: SuperTest) const { type, id } = test.request; await supertest .get(`${getUrlPrefix(spaceId)}/api/saved_objects/${type}/${id}`) - .auth(user?.username, user?.password) + .auth(user?.username!, user?.password!) .expect(test.responseStatusCode) .then(test.responseBody); }); diff --git a/x-pack/test/saved_object_api_integration/common/suites/import.ts b/x-pack/test/saved_object_api_integration/common/suites/import.ts index 7580521e170a6..1af1bf07510b2 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/import.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/import.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { SuperTest } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import type { Client } from '@elastic/elasticsearch'; import type { SavedObjectReference } from '@kbn/core/server'; import { SAVED_OBJECT_TEST_CASES as CASES } from '../lib/saved_object_test_cases'; @@ -121,7 +121,7 @@ export const importTestCaseFailures = { condition !== false ? { failureType: 'missing_references' } : {}, }; -export function importTestSuiteFactory(es: Client, esArchiver: any, supertest: SuperTest) { +export function importTestSuiteFactory(es: Client, esArchiver: any, supertest: SuperTestAgent) { const expectSavedObjectForbidden = (action: string, typeOrTypes: string | string[]) => expectResponses.forbiddenTypes(action)(typeOrTypes); const expectResponseBody = @@ -310,7 +310,7 @@ export function importTestSuiteFactory(es: Client, esArchiver: any, supertest: S : ''; await supertest .post(`${getUrlPrefix(spaceId)}/api/saved_objects/_import${query}`) - .auth(user?.username, user?.password) + .auth(user?.username!, user?.password!) .attach('file', Buffer.from(requestBody, 'utf8'), 'export.ndjson') .expect(test.responseStatusCode) .then(test.responseBody); diff --git a/x-pack/test/saved_object_api_integration/common/suites/resolve.ts b/x-pack/test/saved_object_api_integration/common/suites/resolve.ts index ebb71a860d744..8f03686d4cabc 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/resolve.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/resolve.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { SuperTest } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { SAVED_OBJECT_TEST_CASES as CASES } from '../lib/saved_object_test_cases'; import { SPACES } from '../lib/spaces'; import { @@ -70,7 +70,7 @@ export const TEST_CASES = Object.freeze({ HIDDEN: CASES.HIDDEN, }); -export function resolveTestSuiteFactory(esArchiver: any, supertest: SuperTest) { +export function resolveTestSuiteFactory(esArchiver: any, supertest: SuperTestAgent) { const expectSavedObjectForbidden = expectResponses.forbiddenTypes('bulk_get'); const expectResponseBody = (testCase: ResolveTestCase): ExpectResponseBody => @@ -135,7 +135,7 @@ export function resolveTestSuiteFactory(esArchiver: any, supertest: SuperTest + supertest: SuperTestAgent ) { const expectSavedObjectForbidden = (action: string, typeOrTypes: string | string[]) => expectResponses.forbiddenTypes(action)(typeOrTypes); @@ -373,7 +373,7 @@ export function resolveImportErrorsTestSuiteFactory( const query = test.createNewCopies ? '?createNewCopies=true' : ''; await supertest .post(`${getUrlPrefix(spaceId)}/api/saved_objects/_resolve_import_errors${query}`) - .auth(user?.username, user?.password) + .auth(user?.username!, user?.password!) .field('retries', JSON.stringify(test.request.retries)) .attach('file', Buffer.from(requestBody, 'utf8'), 'export.ndjson') .expect(test.responseStatusCode) diff --git a/x-pack/test/saved_object_api_integration/common/suites/update.ts b/x-pack/test/saved_object_api_integration/common/suites/update.ts index 1fc2cef6e051a..ee37e0feccfe2 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/update.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/update.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { SuperTest } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { SAVED_OBJECT_TEST_CASES as CASES } from '../lib/saved_object_test_cases'; import { SPACES } from '../lib/spaces'; import { expectResponses, getUrlPrefix, getTestTitle } from '../lib/saved_object_test_utils'; @@ -34,7 +34,7 @@ export const TEST_CASES: Record = Object.freeze({ const createRequest = ({ type, id, upsert }: UpdateTestCase) => ({ type, id, upsert }); -export function updateTestSuiteFactory(esArchiver: any, supertest: SuperTest) { +export function updateTestSuiteFactory(esArchiver: any, supertest: SuperTestAgent) { const expectSavedObjectForbidden = expectResponses.forbiddenTypes('update'); const expectResponseBody = (testCase: UpdateTestCase): ExpectResponseBody => @@ -94,7 +94,7 @@ export function updateTestSuiteFactory(esArchiver: any, supertest: SuperTest { + this.timeout(120000); + + for (const targetURL of [ + '///example.com', + '//example.com', + 'https://example.com', + + '/\t/example.com', + '/\n/example.com', + '/\r/example.com', + + '/\t//example.com', + '/\n//example.com', + '/\r//example.com', + + '//\t/example.com', + '//\n/example.com', + '//\r/example.com', + + 'ht\ttps://example.com', + 'ht\ntps://example.com', + 'ht\rtps://example.com', + ]) { + await browser.get( + `${deployment.getHostPort()}/login?next=${encodeURIComponent(targetURL)}` + ); + + await PageObjects.common.waitUntilUrlIncludes('next='); + await PageObjects.security.loginSelector.login('basic', 'basic1'); + // We need to make sure that both path and hash are respected. + const currentURL = parse(await browser.getCurrentUrl()); + + expect(currentURL.pathname).to.eql('/app/home'); + await PageObjects.security.forceLogout(); + } + }); + it('can login with SSO preserving original URL', async () => { await PageObjects.common.navigateToUrl('management', 'security/users', { ensureCurrentUrl: false, diff --git a/x-pack/test/security_functional/tests/oidc/url_capture.ts b/x-pack/test/security_functional/tests/oidc/url_capture.ts index 10ef8ff01c95e..6553ef193fc3b 100644 --- a/x-pack/test/security_functional/tests/oidc/url_capture.ts +++ b/x-pack/test/security_functional/tests/oidc/url_capture.ts @@ -63,5 +63,43 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const currentURL = parse(await browser.getCurrentUrl()); expect(currentURL.path).to.eql('/authentication/app?one=two'); }); + + it('resets invalid target URL', async () => { + this.timeout(120000); + + for (const targetURL of [ + '///example.com', + '//example.com', + 'https://example.com', + + '/\t/example.com', + '/\n/example.com', + '/\r/example.com', + + '/\t//example.com', + '/\n//example.com', + '/\r//example.com', + + '//\t/example.com', + '//\n/example.com', + '//\r/example.com', + + 'ht\ttps://example.com', + 'ht\ntps://example.com', + 'ht\rtps://example.com', + ]) { + await browser.get( + `${deployment.getHostPort()}/internal/security/capture-url?next=${encodeURIComponent( + targetURL + )}` + ); + + await find.byCssSelector('[data-test-subj="userMenuButton"]', 20000); + expect(parse(await browser.getCurrentUrl()).pathname).to.eql('/app/home'); + + await browser.get(deployment.getHostPort() + '/logout'); + await PageObjects.common.waitUntilUrlIncludes('logged_out'); + } + }); }); } diff --git a/x-pack/test/security_functional/tests/saml/url_capture.ts b/x-pack/test/security_functional/tests/saml/url_capture.ts index 1199255f96bf1..0193d3d870701 100644 --- a/x-pack/test/security_functional/tests/saml/url_capture.ts +++ b/x-pack/test/security_functional/tests/saml/url_capture.ts @@ -63,5 +63,43 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const currentURL = parse(await browser.getCurrentUrl()); expect(currentURL.path).to.eql('/authentication/app?one=two'); }); + + it('resets invalid target URL', async () => { + this.timeout(120000); + + for (const targetURL of [ + '///example.com', + '//example.com', + 'https://example.com', + + '/\t/example.com', + '/\n/example.com', + '/\r/example.com', + + '/\t//example.com', + '/\n//example.com', + '/\r//example.com', + + '//\t/example.com', + '//\n/example.com', + '//\r/example.com', + + 'ht\ttps://example.com', + 'ht\ntps://example.com', + 'ht\rtps://example.com', + ]) { + await browser.get( + `${deployment.getHostPort()}/internal/security/capture-url?next=${encodeURIComponent( + targetURL + )}` + ); + + await find.byCssSelector('[data-test-subj="userMenuButton"]', 20000); + expect(parse(await browser.getCurrentUrl()).pathname).to.eql('/app/home'); + + await browser.get(deployment.getHostPort() + '/logout'); + await PageObjects.common.waitUntilUrlIncludes('logged_out'); + } + }); }); } diff --git a/x-pack/test/security_solution_api_integration/config/ess/config.base.ts b/x-pack/test/security_solution_api_integration/config/ess/config.base.ts index cc47b97377e6c..d134546c6f633 100644 --- a/x-pack/test/security_solution_api_integration/config/ess/config.base.ts +++ b/x-pack/test/security_solution_api_integration/config/ess/config.base.ts @@ -80,6 +80,7 @@ export function createTestConfig(options: CreateTestConfigOptions, testFiles?: s '--xpack.ruleRegistry.unsafe.legacyMultiTenancy.enabled=true', `--xpack.securitySolution.enableExperimental=${JSON.stringify([ 'previewTelemetryUrlEnabled', + 'alertSuppressionForEsqlRuleEnabled', 'riskScoringPersistence', 'riskScoringRoutesEnabled', 'bulkCustomHighlightedFieldsEnabled', diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/workflows/basic_license_essentials_tier/create_endpoint_exceptions.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/workflows/basic_license_essentials_tier/create_endpoint_exceptions.ts index 728d5328a9174..53a9cc91a6005 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/workflows/basic_license_essentials_tier/create_endpoint_exceptions.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/workflows/basic_license_essentials_tier/create_endpoint_exceptions.ts @@ -43,7 +43,7 @@ interface Host { * @returns The array of hosts sorted */ export const getHostHits = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, id: string ): Promise => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/configs/serverless.config.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/configs/serverless.config.ts index f6ba7fa49895e..76c73ff71cc18 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/configs/serverless.config.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/configs/serverless.config.ts @@ -19,6 +19,7 @@ export default createTestConfig({ ])}`, // See tests within the file "ignore_fields.ts" which use these values in "alertIgnoreFields" `--xpack.securitySolution.enableExperimental=${JSON.stringify([ 'bulkCustomHighlightedFieldsEnabled', + 'alertSuppressionForEsqlRuleEnabled', ])}`, ], }); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/query.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/custom_query.ts similarity index 100% rename from x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/query.ts rename to x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/custom_query.ts diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/esql_suppression.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/esql_suppression.ts new file mode 100644 index 0000000000000..f7264e064bdba --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/esql_suppression.ts @@ -0,0 +1,2025 @@ +/* + * Copyright 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 sortBy from 'lodash/sortBy'; +import expect from 'expect'; +import { v4 as uuidv4 } from 'uuid'; + +import { + ALERT_SUPPRESSION_START, + ALERT_SUPPRESSION_END, + ALERT_SUPPRESSION_DOCS_COUNT, + ALERT_SUPPRESSION_TERMS, + ALERT_LAST_DETECTED, + TIMESTAMP, + ALERT_START, +} from '@kbn/rule-data-utils'; +import { EsqlRuleCreateProps } from '@kbn/security-solution-plugin/common/api/detection_engine/model/rule_schema'; +import { getCreateEsqlRulesSchemaMock } from '@kbn/security-solution-plugin/common/api/detection_engine/model/rule_schema/mocks'; +import { RuleExecutionStatusEnum } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_monitoring'; +import { ALERT_ORIGINAL_TIME } from '@kbn/security-solution-plugin/common/field_maps/field_names'; +import { DETECTION_ENGINE_SIGNALS_STATUS_URL as DETECTION_ENGINE_ALERTS_STATUS_URL } from '@kbn/security-solution-plugin/common/constants'; +import { getSuppressionMaxSignalsWarning as getSuppressionMaxAlertsWarning } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_types/utils/utils'; + +import { ENABLE_ASSET_CRITICALITY_SETTING } from '@kbn/security-solution-plugin/common/constants'; +import { + getPreviewAlerts, + previewRule, + getOpenAlerts, + dataGeneratorFactory, + previewRuleWithExceptionEntries, + setAlertStatus, + patchRule, +} from '../../../../utils'; +import { + deleteAllRules, + deleteAllAlerts, + createRule, +} from '../../../../../../../common/utils/security_solution'; +import { deleteAllExceptions } from '../../../../../lists_and_exception_lists/utils'; +import { FtrProviderContext } from '../../../../../../ftr_provider_context'; + +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + const es = getService('es'); + const log = getService('log'); + const kibanaServer = getService('kibanaServer'); + const { indexEnhancedDocuments, indexListOfDocuments, indexGeneratedDocuments } = + dataGeneratorFactory({ + es, + index: 'ecs_compliant', + log, + }); + + /** + * to separate docs between rules runs + */ + const internalIdPipe = (id: string) => `| where id=="${id}"`; + + const getNonAggRuleQueryWithMetadata = (id: string) => + `from ecs_compliant metadata _id, _index, _version ${internalIdPipe(id)}`; + + // skipped in MKI as it depends on feature flag alertSuppressionForEsqlRuleEnabled + describe('@ess @serverless @skipInServerlessMKI ES|QL rule type, alert suppression', () => { + before(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/security_solution/ecs_compliant'); + }); + + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/security_solution/ecs_compliant'); + await deleteAllAlerts(supertest, log, es); + await deleteAllRules(supertest, log); + }); + + it('should suppress an alert during real rule executions', async () => { + const id = uuidv4(); + const firstTimestamp = new Date().toISOString(); + + const firstExecutionDocuments = [ + { + host: { name: 'host-0' }, + id, + '@timestamp': firstTimestamp, + }, + { + host: { name: 'host-0' }, + id, + '@timestamp': firstTimestamp, + }, + { + host: { name: 'host-0' }, + id, + '@timestamp': firstTimestamp, + }, + ]; + + await indexListOfDocuments(firstExecutionDocuments); + + const rule: EsqlRuleCreateProps = { + ...getCreateEsqlRulesSchemaMock('rule-1', true), + query: getNonAggRuleQueryWithMetadata(id), + from: 'now-35m', + interval: '30m', + alert_suppression: { + group_by: ['host.name'], + duration: { + value: 300, + unit: 'm', + }, + missing_fields_strategy: 'suppress', + }, + }; + + const createdRule = await createRule(supertest, log, rule); + const alerts = await getOpenAlerts(supertest, log, es, createdRule); + + expect(alerts.hits.hits.length).toBe(1); + expect(alerts.hits.hits[0]._source).toEqual( + expect.objectContaining({ + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'host.name', + value: 'host-0', + }, + ], + // suppression boundaries equal to original event time, since no alert been suppressed + [ALERT_SUPPRESSION_START]: firstTimestamp, + [ALERT_SUPPRESSION_END]: firstTimestamp, + [ALERT_ORIGINAL_TIME]: firstTimestamp, + [ALERT_SUPPRESSION_DOCS_COUNT]: 2, + }) + ); + + const secondTimestamp = new Date().toISOString(); + const secondExecutionDocuments = [ + { + host: { name: 'host-0', ip: '127.0.0.5' }, + id, + '@timestamp': secondTimestamp, + }, + ]; + // Add a new document, then disable and re-enable to trigger another rule run. The second doc should + // trigger an update to the existing alert without changing the timestamp + await indexListOfDocuments(secondExecutionDocuments); + + await patchRule(supertest, log, { id: createdRule.id, enabled: false }); + await patchRule(supertest, log, { id: createdRule.id, enabled: true }); + const afterTimestamp = new Date(); + const secondAlerts = await getOpenAlerts( + supertest, + log, + es, + createdRule, + RuleExecutionStatusEnum.succeeded, + undefined, + afterTimestamp + ); + expect(secondAlerts.hits.hits.length).toEqual(1); + expect(secondAlerts.hits.hits[0]._source).toEqual( + expect.objectContaining({ + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'host.name', + value: 'host-0', + }, + ], + [ALERT_ORIGINAL_TIME]: firstTimestamp, // timestamp is the same + [ALERT_SUPPRESSION_START]: firstTimestamp, // suppression start is the same + [ALERT_SUPPRESSION_END]: secondTimestamp, // suppression end is updated + [ALERT_SUPPRESSION_DOCS_COUNT]: 3, + }) + ); + }); + + it('should NOT suppress and update an alert if the alert is closed', async () => { + const id = uuidv4(); + const firstTimestamp = new Date().toISOString(); + + const rule: EsqlRuleCreateProps = { + ...getCreateEsqlRulesSchemaMock('rule-1', true), + query: getNonAggRuleQueryWithMetadata(id), + from: 'now-35m', + interval: '30m', + alert_suppression: { + group_by: ['host.name'], + duration: { + value: 300, + unit: 'm', + }, + missing_fields_strategy: 'suppress', + }, + }; + + const firstExecutionDocuments = [ + { + host: { name: 'host-0' }, + id, + '@timestamp': firstTimestamp, + }, + ]; + + await indexListOfDocuments(firstExecutionDocuments); + + const createdRule = await createRule(supertest, log, rule); + const alerts = await getOpenAlerts(supertest, log, es, createdRule); + expect(alerts.hits.hits).toHaveLength(1); + // Close the alert. Subsequent rule executions should ignore this closed alert + // for suppression purposes. + const alertIds = alerts.hits.hits.map((alert) => alert._id); + await supertest + .post(DETECTION_ENGINE_ALERTS_STATUS_URL) + .set('kbn-xsrf', 'true') + .send(setAlertStatus({ alertIds, status: 'closed' })) + .expect(200); + + const secondTimestamp = new Date().toISOString(); + const secondExecutionDocuments = [ + { + host: { name: 'host-0' }, + id, + '@timestamp': secondTimestamp, + }, + ]; + // Add new documents, then disable and re-enable to trigger another rule run. The second doc should + // trigger a new alert since the first one is now closed. + await indexListOfDocuments(secondExecutionDocuments); + + await patchRule(supertest, log, { id: createdRule.id, enabled: false }); + await patchRule(supertest, log, { id: createdRule.id, enabled: true }); + const afterTimestamp = new Date(); + const secondAlerts = await getOpenAlerts( + supertest, + log, + es, + createdRule, + RuleExecutionStatusEnum.succeeded, + undefined, + afterTimestamp + ); + expect(secondAlerts.hits.hits.length).toEqual(1); + expect(alerts.hits.hits[0]._source).toEqual( + expect.objectContaining({ + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'host.name', + value: 'host-0', + }, + ], + [ALERT_ORIGINAL_TIME]: firstTimestamp, + [ALERT_SUPPRESSION_DOCS_COUNT]: 0, + }) + ); + }); + + it('should NOT suppress alerts when suppression period is less than rule interval', async () => { + const id = uuidv4(); + const firstTimestamp = '2020-10-28T05:45:00.000Z'; + const secondTimestamp = '2020-10-28T06:15:00.000Z'; + + const firstExecutionDocuments = [ + { + host: { name: 'host-0' }, + id, + '@timestamp': firstTimestamp, + }, + ]; + const secondExecutionDocuments = [ + { + host: { name: 'host-0' }, + id, + '@timestamp': secondTimestamp, + }, + ]; + await indexListOfDocuments([...firstExecutionDocuments, ...secondExecutionDocuments]); + + const rule: EsqlRuleCreateProps = { + ...getCreateEsqlRulesSchemaMock('rule-1', true), + query: getNonAggRuleQueryWithMetadata(id), + from: 'now-35m', + interval: '30m', + alert_suppression: { + group_by: ['host.name'], + duration: { + value: 10, + unit: 'm', + }, + missing_fields_strategy: 'suppress', + }, + }; + + const { previewId } = await previewRule({ + supertest, + rule, + timeframeEnd: new Date('2020-10-28T06:30:00.000Z'), + invocationCount: 2, + }); + + const previewAlerts = await getPreviewAlerts({ + es, + previewId, + sort: [ALERT_ORIGINAL_TIME], + }); + expect(previewAlerts.length).toBe(2); + expect(previewAlerts[0]._source).toEqual( + expect.objectContaining({ + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'host.name', + value: 'host-0', + }, + ], + [TIMESTAMP]: '2020-10-28T06:00:00.000Z', + [ALERT_SUPPRESSION_START]: firstTimestamp, + [ALERT_SUPPRESSION_END]: firstTimestamp, + [ALERT_SUPPRESSION_DOCS_COUNT]: 0, + }) + ); + + expect(previewAlerts[1]._source).toEqual( + expect.objectContaining({ + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'host.name', + value: 'host-0', + }, + ], + [TIMESTAMP]: '2020-10-28T06:30:00.000Z', + [ALERT_SUPPRESSION_START]: secondTimestamp, + [ALERT_SUPPRESSION_END]: secondTimestamp, + [ALERT_SUPPRESSION_DOCS_COUNT]: 0, + }) + ); + }); + + it('should suppress alerts in the time window that covers 3 rule executions', async () => { + const id = uuidv4(); + const firstTimestamp = '2020-10-28T05:45:00.000Z'; + const secondTimestamp = '2020-10-28T06:15:00.000Z'; + const thirdTimestamp = '2020-10-28T06:45:00.000Z'; + + const firstExecutionDocuments = [ + { + host: { name: 'host-0' }, + id, + '@timestamp': firstTimestamp, + }, + { + host: { name: 'host-0' }, + id, + '@timestamp': firstTimestamp, + }, + ]; + const secondExecutionDocuments = [ + { + host: { name: 'host-0' }, + id, + '@timestamp': secondTimestamp, + }, + ]; + const thirdExecutionDocuments = [ + { + host: { name: 'host-0' }, + id, + '@timestamp': thirdTimestamp, + }, + ]; + + await indexListOfDocuments([ + ...firstExecutionDocuments, + ...secondExecutionDocuments, + ...thirdExecutionDocuments, + ]); + + const rule: EsqlRuleCreateProps = { + ...getCreateEsqlRulesSchemaMock('rule-1', true), + query: getNonAggRuleQueryWithMetadata(id), + from: 'now-35m', + interval: '30m', + alert_suppression: { + group_by: ['host.name'], + duration: { + value: 2, + unit: 'h', + }, + missing_fields_strategy: 'suppress', + }, + }; + + const { previewId } = await previewRule({ + supertest, + rule, + timeframeEnd: new Date('2020-10-28T07:00:00.000Z'), + invocationCount: 3, + }); + const previewAlerts = await getPreviewAlerts({ + es, + previewId, + sort: [ALERT_ORIGINAL_TIME], + }); + expect(previewAlerts.length).toEqual(1); + expect(previewAlerts[0]._source).toEqual({ + ...previewAlerts[0]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'host.name', + value: 'host-0', + }, + ], + [TIMESTAMP]: '2020-10-28T06:00:00.000Z', + [ALERT_LAST_DETECTED]: '2020-10-28T07:00:00.000Z', // Note: ALERT_LAST_DETECTED gets updated, timestamp does not + [ALERT_START]: '2020-10-28T06:00:00.000Z', + [ALERT_ORIGINAL_TIME]: firstTimestamp, + [ALERT_SUPPRESSION_START]: firstTimestamp, + [ALERT_SUPPRESSION_END]: thirdTimestamp, + [ALERT_SUPPRESSION_DOCS_COUNT]: 3, // in total 3 alert got suppressed: 1 from the first run, 1 from the second, 1 from the third + }); + }); + + it('should suppress the correct alerts based on multi values group_by', async () => { + const id = uuidv4(); + const firstTimestamp = '2020-10-28T05:45:00.000Z'; + const secondTimestamp = '2020-10-28T06:15:00.000Z'; + + const firstExecutionDocuments = [ + { + host: { name: 'host-a' }, + id, + '@timestamp': firstTimestamp, + 'agent.version': 1, + }, + { + host: { name: 'host-a' }, + id, + '@timestamp': firstTimestamp, + 'agent.version': 2, + }, + { + host: { name: 'host-b' }, + id, + '@timestamp': firstTimestamp, + 'agent.version': 2, + }, + ]; + const secondExecutionDocuments = [ + { + host: { name: 'host-a' }, + id, + '@timestamp': secondTimestamp, + 'agent.version': 1, + }, + ]; + + await indexListOfDocuments([...firstExecutionDocuments, ...secondExecutionDocuments]); + + const rule: EsqlRuleCreateProps = { + ...getCreateEsqlRulesSchemaMock('rule-1', true), + query: `from ecs_compliant metadata _id, _index, _version ${internalIdPipe( + id + )} | where host.name=="host-a"`, + from: 'now-35m', + interval: '30m', + alert_suppression: { + group_by: ['host.name', 'agent.version'], + duration: { + value: 60, + unit: 'm', + }, + missing_fields_strategy: 'suppress', + }, + }; + + const { previewId } = await previewRule({ + supertest, + rule, + timeframeEnd: new Date('2020-10-28T06:30:00.000Z'), + invocationCount: 2, + }); + + const previewAlerts = await getPreviewAlerts({ + es, + previewId, + sort: ['host.name', 'agent.version', ALERT_ORIGINAL_TIME], + }); + // 3 alerts should be generated: + // 1. for pair 'host-a', 1 - suppressed + // 2. for pair 'host-a', 2 - not suppressed + expect(previewAlerts.length).toEqual(2); + expect(previewAlerts[0]._source).toEqual({ + ...previewAlerts[0]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'host.name', + value: 'host-a', + }, + { + field: 'agent.version', + value: '1', + }, + ], + [TIMESTAMP]: '2020-10-28T06:00:00.000Z', + [ALERT_ORIGINAL_TIME]: firstTimestamp, + [ALERT_SUPPRESSION_START]: firstTimestamp, + [ALERT_SUPPRESSION_END]: secondTimestamp, + [ALERT_SUPPRESSION_DOCS_COUNT]: 1, + }); + expect(previewAlerts[1]._source).toEqual({ + ...previewAlerts[1]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'host.name', + value: 'host-a', + }, + { + field: 'agent.version', + value: '2', + }, + ], + [TIMESTAMP]: '2020-10-28T06:00:00.000Z', + [ALERT_LAST_DETECTED]: '2020-10-28T06:00:00.000Z', + [ALERT_ORIGINAL_TIME]: firstTimestamp, + [ALERT_SUPPRESSION_START]: firstTimestamp, + [ALERT_SUPPRESSION_END]: firstTimestamp, + [ALERT_SUPPRESSION_DOCS_COUNT]: 0, // no suppressed alerts + }); + }); + + it('should correctly suppress when using a timestamp override', async () => { + const id = uuidv4(); + const firstTimestamp = '2020-10-28T05:45:00.000Z'; + const secondTimestamp = '2020-10-28T06:10:00.000Z'; + + const docWithoutOverride = { + id, + '@timestamp': firstTimestamp, + host: { name: 'host-a' }, + }; + const docWithOverride = { + ...docWithoutOverride, + host: { name: 'host-a' }, + // This simulates a very late arriving doc + '@timestamp': '2020-10-28T03:00:00.000Z', + event: { + ingested: secondTimestamp, + }, + }; + + await indexListOfDocuments([docWithoutOverride, docWithOverride]); + + const rule: EsqlRuleCreateProps = { + ...getCreateEsqlRulesSchemaMock('rule-1', true), + query: getNonAggRuleQueryWithMetadata(id), + from: 'now-35m', + interval: '30m', + alert_suppression: { + group_by: ['host.name'], + duration: { + value: 60, + unit: 'm', + }, + missing_fields_strategy: 'suppress', + }, + timestamp_override: 'event.ingested', + }; + + // 1 alert should be suppressed, based on event.ingested value of a document + const { previewId } = await previewRule({ + supertest, + rule, + timeframeEnd: new Date('2020-10-28T06:30:00.000Z'), + invocationCount: 2, + }); + const previewAlerts = await getPreviewAlerts({ + es, + previewId, + sort: ['host.name', ALERT_ORIGINAL_TIME], + }); + + expect(previewAlerts.length).toEqual(1); + expect(previewAlerts[0]._source).toEqual({ + ...previewAlerts[0]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'host.name', + value: 'host-a', + }, + ], + [ALERT_ORIGINAL_TIME]: firstTimestamp, + [ALERT_SUPPRESSION_START]: firstTimestamp, + [ALERT_SUPPRESSION_END]: secondTimestamp, + [ALERT_SUPPRESSION_DOCS_COUNT]: 1, + }); + }); + + it('should deduplicate multiple alerts while suppressing new ones', async () => { + const id = uuidv4(); + const firstTimestamp = '2020-10-28T05:45:00.000Z'; + const secondTimestamp = '2020-10-28T06:10:00.000Z'; + + const firstExecutionDocuments = [ + { + host: { name: 'host-a' }, + id, + '@timestamp': firstTimestamp, + }, + { + host: { name: 'host-a' }, + id, + '@timestamp': firstTimestamp, + }, + ]; + const secondExecutionDocuments = [ + { + host: { name: 'host-a' }, + id, + '@timestamp': secondTimestamp, + }, + { + host: { name: 'host-a' }, + id, + '@timestamp': secondTimestamp, + }, + { + host: { name: 'host-a' }, + id, + '@timestamp': secondTimestamp, + }, + ]; + + await indexListOfDocuments([...firstExecutionDocuments, ...secondExecutionDocuments]); + + const rule: EsqlRuleCreateProps = { + ...getCreateEsqlRulesSchemaMock('rule-1', true), + query: getNonAggRuleQueryWithMetadata(id), + alert_suppression: { + group_by: ['host.name'], + duration: { + value: 60, + unit: 'm', + }, + missing_fields_strategy: 'suppress', + }, + // large look-back time covers all docs + from: 'now-1h', + interval: '30m', + }; + + const { previewId } = await previewRule({ + supertest, + rule, + timeframeEnd: new Date('2020-10-28T06:30:00.000Z'), + invocationCount: 2, + }); + const previewAlerts = await getPreviewAlerts({ + es, + previewId, + sort: ['host.name', ALERT_ORIGINAL_TIME], + }); + expect(previewAlerts.length).toEqual(1); + expect(previewAlerts[0]._source).toEqual({ + ...previewAlerts[0]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'host.name', + value: 'host-a', + }, + ], + [ALERT_ORIGINAL_TIME]: firstTimestamp, + [ALERT_SUPPRESSION_START]: firstTimestamp, + [ALERT_SUPPRESSION_END]: secondTimestamp, + [ALERT_SUPPRESSION_DOCS_COUNT]: 4, + }); + }); + + it('should suppress alerts with missing fields', async () => { + const id = uuidv4(); + const firstTimestamp = '2020-10-28T05:45:00.000Z'; + const secondTimestamp = '2020-10-28T06:10:00.000Z'; + + const firstExecutionDocuments = [ + { + id, + '@timestamp': firstTimestamp, + host: { name: 'host-a', ip: '127.0.0.3' }, + }, + { + id, + '@timestamp': firstTimestamp, + host: { name: 'host-a', ip: '127.0.0.4' }, + }, + { + id, + '@timestamp': firstTimestamp, + host: { ip: '127.0.0.5' }, // doc 1 with missing host.name field + }, + { + id, + '@timestamp': firstTimestamp, + host: { ip: '127.0.0.6' }, // doc 2 with missing host.name field + }, + ]; + const secondExecutionDocuments = [ + { + host: { name: 'host-a', ip: '127.0.0.10' }, + id, + '@timestamp': secondTimestamp, + }, + { + host: { ip: '127.0.0.11' }, // doc 3 with missing host.name field + id, + '@timestamp': secondTimestamp, + }, + ]; + + await indexListOfDocuments([...firstExecutionDocuments, ...secondExecutionDocuments]); + + const rule: EsqlRuleCreateProps = { + ...getCreateEsqlRulesSchemaMock('rule-1', true), + query: getNonAggRuleQueryWithMetadata(id), + from: 'now-35m', + interval: '30m', + alert_suppression: { + group_by: ['host.name'], + duration: { + value: 60, + unit: 'm', + }, + missing_fields_strategy: 'suppress', + }, + }; + + const { previewId } = await previewRule({ + supertest, + rule, + timeframeEnd: new Date('2020-10-28T06:30:00.000Z'), + invocationCount: 2, + }); + const previewAlerts = await getPreviewAlerts({ + es, + previewId, + sort: ['host.name', ALERT_ORIGINAL_TIME], + }); + expect(previewAlerts.length).toEqual(2); + expect(previewAlerts[0]._source).toEqual({ + ...previewAlerts[0]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'host.name', + value: 'host-a', + }, + ], + [ALERT_ORIGINAL_TIME]: firstTimestamp, + [ALERT_SUPPRESSION_START]: firstTimestamp, + [ALERT_SUPPRESSION_END]: secondTimestamp, + [ALERT_SUPPRESSION_DOCS_COUNT]: 2, + }); + + expect(previewAlerts[1]._source).toEqual({ + ...previewAlerts[1]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'host.name', + value: null, + }, + ], + [ALERT_ORIGINAL_TIME]: firstTimestamp, + [ALERT_SUPPRESSION_START]: firstTimestamp, + [ALERT_SUPPRESSION_END]: secondTimestamp, + [ALERT_SUPPRESSION_DOCS_COUNT]: 2, + }); + }); + + it('should not suppress alerts with missing fields if configured so', async () => { + const id = uuidv4(); + const firstTimestamp = '2020-10-28T05:45:00.000Z'; + const secondTimestamp = '2020-10-28T06:10:00.000Z'; + + const firstExecutionDocuments = [ + { + id, + '@timestamp': firstTimestamp, + host: { name: 'host-a', ip: '127.0.0.3' }, + }, + { + id, + '@timestamp': firstTimestamp, + host: { name: 'host-a', ip: '127.0.0.4' }, + }, + { + id, + '@timestamp': firstTimestamp, + host: { ip: '127.0.0.5' }, // doc 1 with missing host.name field + }, + { + id, + '@timestamp': firstTimestamp, + host: { ip: '127.0.0.6' }, // doc 2 with missing host.name field + }, + ]; + const secondExecutionDocuments = [ + { + host: { name: 'host-a', ip: '127.0.0.10' }, + id, + '@timestamp': secondTimestamp, + }, + { + host: { ip: '127.0.0.11' }, // doc 3 with missing host.name field + id, + '@timestamp': secondTimestamp, + }, + ]; + + await indexListOfDocuments([...firstExecutionDocuments, ...secondExecutionDocuments]); + + const rule: EsqlRuleCreateProps = { + ...getCreateEsqlRulesSchemaMock('rule-1', true), + query: getNonAggRuleQueryWithMetadata(id), + from: 'now-35m', + interval: '30m', + alert_suppression: { + group_by: ['host.name'], + duration: { + value: 60, + unit: 'm', + }, + missing_fields_strategy: 'doNotSuppress', + }, + }; + + const { previewId } = await previewRule({ + supertest, + rule, + timeframeEnd: new Date('2020-10-28T06:30:00.000Z'), + invocationCount: 2, + }); + const previewAlerts = await getPreviewAlerts({ + es, + previewId, + sort: ['host.name', ALERT_ORIGINAL_TIME], + }); + expect(previewAlerts.length).toEqual(4); + expect(previewAlerts[0]._source).toEqual({ + ...previewAlerts[0]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'host.name', + value: 'host-a', + }, + ], + [ALERT_ORIGINAL_TIME]: firstTimestamp, + [ALERT_SUPPRESSION_START]: firstTimestamp, + [ALERT_SUPPRESSION_END]: secondTimestamp, + [ALERT_SUPPRESSION_DOCS_COUNT]: 2, + }); + + // rest of alerts are not suppressed and do not have suppress properties + previewAlerts.slice(1).forEach((previewAlert) => { + const source = previewAlert._source; + expect(source).toHaveProperty('id', id); + expect(source).not.toHaveProperty(ALERT_SUPPRESSION_DOCS_COUNT); + expect(source).not.toHaveProperty(ALERT_SUPPRESSION_END); + expect(source).not.toHaveProperty(ALERT_SUPPRESSION_TERMS); + expect(source).not.toHaveProperty(ALERT_SUPPRESSION_DOCS_COUNT); + }); + }); + + it('should suppress alerts for aggregating queries', async () => { + const id = uuidv4(); + const firstTimestamp = '2020-10-28T05:45:00.000Z'; + const secondTimestamp = '2020-10-28T06:10:00.000Z'; + + const firstExecutionDocuments = [ + { + id, + '@timestamp': firstTimestamp, + host: { name: 'host-a' }, + }, + { + id, + '@timestamp': firstTimestamp, + host: { name: 'host-a' }, + }, + ]; + const secondExecutionDocuments = [ + { + host: { name: 'host-a' }, + id, + '@timestamp': secondTimestamp, + }, + { + host: { name: 'host-a' }, + id, + '@timestamp': secondTimestamp, + }, + { + host: { name: 'host-a' }, + id, + '@timestamp': secondTimestamp, + }, + ]; + + await indexListOfDocuments([...firstExecutionDocuments, ...secondExecutionDocuments]); + + const rule: EsqlRuleCreateProps = { + ...getCreateEsqlRulesSchemaMock('rule-1', true), + query: `from ecs_compliant ${internalIdPipe( + id + )} | stats counted_agents=count(host.name) by host.name`, + from: 'now-35m', + interval: '30m', + alert_suppression: { + group_by: ['host.name'], + duration: { + value: 60, + unit: 'm', + }, + missing_fields_strategy: 'suppress', + }, + }; + + const { previewId } = await previewRule({ + supertest, + rule, + timeframeEnd: new Date('2020-10-28T06:30:00.000Z'), + invocationCount: 2, + }); + const previewAlerts = await getPreviewAlerts({ + es, + previewId, + sort: ['host.name', ALERT_ORIGINAL_TIME], + }); + expect(previewAlerts.length).toEqual(1); + expect(previewAlerts[0]._source).toEqual({ + ...previewAlerts[0]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'host.name', + value: 'host-a', + }, + ], + [ALERT_SUPPRESSION_START]: '2020-10-28T06:00:00.000Z', // since aggregation query results do not have timestamp properties suppression boundary start set as a first execution time + [ALERT_SUPPRESSION_END]: '2020-10-28T06:30:00.000Z', // since aggregation query results do not have timestamp properties suppression boundary end set as a second execution time + [ALERT_SUPPRESSION_DOCS_COUNT]: 1, // only one suppressed alert, since aggregation query produces one alert per rule execution, no matter how many events aggregated + }); + expect(previewAlerts[0]._source).not.toHaveProperty(ALERT_ORIGINAL_TIME); + }); + + it('should suppress alerts by custom field, created in ES|QL query', async () => { + const id = uuidv4(); + const firstTimestamp = '2020-10-28T05:45:00.000Z'; + const secondTimestamp = '2020-10-28T06:10:00.000Z'; + + const firstExecutionDocuments = [ + { + id, + '@timestamp': firstTimestamp, + host: { name: 'host-a' }, + }, + { + id, + '@timestamp': firstTimestamp, + host: { name: 'host-b' }, + }, + ]; + const secondExecutionDocuments = [ + { + host: { name: 'test-c' }, + id, + '@timestamp': secondTimestamp, + }, + { + host: { name: 'host-d' }, + id, + '@timestamp': secondTimestamp, + }, + { + host: { name: 'test-s' }, + id, + '@timestamp': secondTimestamp, + }, + ]; + + await indexListOfDocuments([...firstExecutionDocuments, ...secondExecutionDocuments]); + + const rule: EsqlRuleCreateProps = { + ...getCreateEsqlRulesSchemaMock('rule-1', true), + // ES|QL query creates new field custom_field - prefix_host, prefix_test + query: `from ecs_compliant metadata _id ${internalIdPipe( + id + )} | eval custom_field=concat("prefix_", left(host.name, 4))`, + from: 'now-35m', + interval: '30m', + alert_suppression: { + group_by: ['custom_field'], + duration: { + value: 60, + unit: 'm', + }, + missing_fields_strategy: 'suppress', + }, + }; + + const { previewId } = await previewRule({ + supertest, + rule, + timeframeEnd: new Date('2020-10-28T06:30:00.000Z'), + invocationCount: 2, + }); + const previewAlerts = await getPreviewAlerts({ + es, + previewId, + }); + // lodash sortBy is used here because custom_field is non ECS and not mapped in alerts index, so can't be sorted by + const sortedAlerts = sortBy(previewAlerts, 'custom_field'); + expect(previewAlerts.length).toEqual(2); + + expect(sortedAlerts[0]._source).toEqual({ + ...sortedAlerts[0]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'custom_field', + value: 'prefix_host', + }, + ], + [ALERT_ORIGINAL_TIME]: firstTimestamp, + [ALERT_SUPPRESSION_START]: firstTimestamp, + [ALERT_SUPPRESSION_END]: secondTimestamp, + [ALERT_SUPPRESSION_DOCS_COUNT]: 2, + }); + + expect(sortedAlerts[1]._source).toEqual({ + ...sortedAlerts[1]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'custom_field', + value: 'prefix_test', + }, + ], + [ALERT_ORIGINAL_TIME]: secondTimestamp, + [ALERT_SUPPRESSION_START]: secondTimestamp, + [ALERT_SUPPRESSION_END]: secondTimestamp, + [ALERT_SUPPRESSION_DOCS_COUNT]: 1, + }); + }); + + it('should suppress alerts by custom field, created in ES|QL query, when do not suppress missing fields configured', async () => { + const id = uuidv4(); + const firstTimestamp = '2020-10-28T05:45:00.000Z'; + const secondTimestamp = '2020-10-28T06:10:00.000Z'; + + const firstExecutionDocuments = [ + { + id, + '@timestamp': firstTimestamp, + host: { name: 'host-a' }, + }, + { + id, + '@timestamp': firstTimestamp, + host: { name: 'host-b' }, + }, + ]; + const secondExecutionDocuments = [ + { + host: { name: 'test-c' }, + id, + '@timestamp': secondTimestamp, + }, + { + host: { name: 'host-d' }, + id, + '@timestamp': secondTimestamp, + }, + { + host: { name: 'test-s' }, + id, + '@timestamp': secondTimestamp, + }, + ]; + + await indexListOfDocuments([...firstExecutionDocuments, ...secondExecutionDocuments]); + + const rule: EsqlRuleCreateProps = { + ...getCreateEsqlRulesSchemaMock('rule-1', true), + // ES|QL query creates new field custom_field - prefix_host, prefix_test + query: `from ecs_compliant metadata _id ${internalIdPipe( + id + )} | eval custom_field=concat("prefix_", left(host.name, 4))`, + from: 'now-35m', + interval: '30m', + alert_suppression: { + group_by: ['custom_field'], + duration: { + value: 60, + unit: 'm', + }, + missing_fields_strategy: 'doNotSuppress', + }, + }; + + const { previewId } = await previewRule({ + supertest, + rule, + timeframeEnd: new Date('2020-10-28T06:30:00.000Z'), + invocationCount: 2, + }); + const previewAlerts = await getPreviewAlerts({ + es, + previewId, + }); + // lodash sortBy is used here because custom_field is non ECS and not mapped in alerts index, so can't be sorted by + const sortedAlerts = sortBy(previewAlerts, 'custom_field'); + expect(previewAlerts.length).toEqual(2); + + expect(sortedAlerts[0]._source).toEqual({ + ...sortedAlerts[0]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'custom_field', + value: 'prefix_host', + }, + ], + [ALERT_ORIGINAL_TIME]: firstTimestamp, + [ALERT_SUPPRESSION_START]: firstTimestamp, + [ALERT_SUPPRESSION_END]: secondTimestamp, + [ALERT_SUPPRESSION_DOCS_COUNT]: 2, + }); + + expect(sortedAlerts[1]._source).toEqual({ + ...sortedAlerts[1]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'custom_field', + value: 'prefix_test', + }, + ], + [ALERT_ORIGINAL_TIME]: secondTimestamp, + [ALERT_SUPPRESSION_START]: secondTimestamp, + [ALERT_SUPPRESSION_END]: secondTimestamp, + [ALERT_SUPPRESSION_DOCS_COUNT]: 1, + }); + }); + + it('should suppress by field, dropped in ES|QL query, but returned from source index', async () => { + const id = uuidv4(); + const firstTimestamp = '2020-10-28T05:45:00.000Z'; + const secondTimestamp = '2020-10-28T06:10:00.000Z'; + + const firstExecutionDocuments = [ + { + id, + '@timestamp': firstTimestamp, + host: { name: 'host-a' }, + }, + ]; + const secondExecutionDocuments = [ + { + host: { name: 'host-a' }, + id, + '@timestamp': secondTimestamp, + }, + ]; + + await indexListOfDocuments([...firstExecutionDocuments, ...secondExecutionDocuments]); + + const rule: EsqlRuleCreateProps = { + ...getCreateEsqlRulesSchemaMock('rule-1', true), + query: `from ecs_compliant metadata _id ${internalIdPipe(id)} | drop host.name`, + from: 'now-35m', + interval: '30m', + alert_suppression: { + group_by: ['host.name'], + duration: { + value: 60, + unit: 'm', + }, + missing_fields_strategy: 'suppress', + }, + }; + + const { previewId } = await previewRule({ + supertest, + rule, + timeframeEnd: new Date('2020-10-28T06:30:00.000Z'), + invocationCount: 2, + }); + const previewAlerts = await getPreviewAlerts({ + es, + previewId, + sort: ['host.name', ALERT_ORIGINAL_TIME], + }); + expect(previewAlerts.length).toEqual(1); + + expect(previewAlerts[0]._source).toEqual({ + ...previewAlerts[0]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'host.name', + value: ['host-a'], + }, + ], + [ALERT_ORIGINAL_TIME]: firstTimestamp, + [ALERT_SUPPRESSION_START]: firstTimestamp, + [ALERT_SUPPRESSION_END]: secondTimestamp, + [ALERT_SUPPRESSION_DOCS_COUNT]: 1, + }); + }); + + // even when field is dropped in ES|QL query it is returned from source document + it('should not suppress alerts by field, dropped in ES|QL query, when do not suppress missing fields configured', async () => { + const id = uuidv4(); + const firstTimestamp = '2020-10-28T05:45:00.000Z'; + const secondTimestamp = '2020-10-28T06:10:00.000Z'; + + const firstExecutionDocuments = [ + { + id, + '@timestamp': firstTimestamp, + host: { name: 'host-a' }, + }, + { + id, + '@timestamp': firstTimestamp, + }, + ]; + const secondExecutionDocuments = [ + { + host: { name: 'host-a' }, + id, + '@timestamp': secondTimestamp, + }, + { + id, + '@timestamp': firstTimestamp, + }, + ]; + + await indexListOfDocuments([...firstExecutionDocuments, ...secondExecutionDocuments]); + + const rule: EsqlRuleCreateProps = { + ...getCreateEsqlRulesSchemaMock('rule-1', true), + query: `from ecs_compliant metadata _id ${internalIdPipe(id)} | drop host.name`, + from: 'now-35m', + interval: '30m', + alert_suppression: { + group_by: ['host.name'], + duration: { + value: 60, + unit: 'm', + }, + missing_fields_strategy: 'doNotSuppress', + }, + }; + + const { previewId } = await previewRule({ + supertest, + rule, + timeframeEnd: new Date('2020-10-28T06:30:00.000Z'), + invocationCount: 2, + }); + const previewAlerts = await getPreviewAlerts({ + es, + previewId, + sort: ['host.name', ALERT_ORIGINAL_TIME], + }); + expect(previewAlerts.length).toEqual(3); + + expect(previewAlerts[0]._source).toEqual({ + ...previewAlerts[0]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'host.name', + value: ['host-a'], + }, + ], + [ALERT_ORIGINAL_TIME]: firstTimestamp, + [ALERT_SUPPRESSION_START]: firstTimestamp, + [ALERT_SUPPRESSION_END]: secondTimestamp, + [ALERT_SUPPRESSION_DOCS_COUNT]: 1, + }); + + // rest of alerts are not suppressed and do not have suppress properties + previewAlerts.slice(1).forEach((previewAlert) => { + const source = previewAlert._source; + expect(source).toHaveProperty('id', id); + expect(source).not.toHaveProperty(ALERT_SUPPRESSION_DOCS_COUNT); + expect(source).not.toHaveProperty(ALERT_SUPPRESSION_END); + expect(source).not.toHaveProperty(ALERT_SUPPRESSION_TERMS); + expect(source).not.toHaveProperty(ALERT_SUPPRESSION_DOCS_COUNT); + }); + }); + + describe('rule execution only', () => { + it('should suppress alerts during rule execution only', async () => { + const id = uuidv4(); + const timestamp = '2020-10-28T06:45:00.000Z'; + const laterTimestamp = '2020-10-28T06:50:00.000Z'; + + const firstExecutionDocuments = [ + { + host: { name: 'host-a' }, + id, + '@timestamp': timestamp, + }, + { + host: { name: 'host-a' }, + id, + '@timestamp': timestamp, + }, + { + host: { name: 'host-a' }, + id, + '@timestamp': laterTimestamp, + }, + // does not generate alert + { + host: { name: 'host-b' }, + id, + '@timestamp': laterTimestamp, + }, + ]; + + await indexListOfDocuments(firstExecutionDocuments); + + const rule: EsqlRuleCreateProps = { + ...getCreateEsqlRulesSchemaMock('rule-1', true), + query: `from ecs_compliant metadata _id, _index, _version ${internalIdPipe( + id + )} | where host.name=="host-a"`, + alert_suppression: { + group_by: ['host.name'], + missing_fields_strategy: 'suppress', + }, + from: 'now-35m', + interval: '30m', + }; + + const { previewId } = await previewRule({ + supertest, + rule, + timeframeEnd: new Date('2020-10-28T07:00:00.000Z'), + invocationCount: 1, + }); + const previewAlerts = await getPreviewAlerts({ + es, + previewId, + sort: [ALERT_ORIGINAL_TIME], + }); + expect(previewAlerts.length).toEqual(1); + expect(previewAlerts[0]._source).toEqual({ + ...previewAlerts[0]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'host.name', + value: 'host-a', + }, + ], + [TIMESTAMP]: '2020-10-28T07:00:00.000Z', + [ALERT_LAST_DETECTED]: '2020-10-28T07:00:00.000Z', + [ALERT_ORIGINAL_TIME]: timestamp, + [ALERT_SUPPRESSION_START]: timestamp, + [ALERT_SUPPRESSION_END]: laterTimestamp, // suppression ends with later timestamp + [ALERT_SUPPRESSION_DOCS_COUNT]: 2, + }); + }); + + it('should suppress alerts per rule execution for array field', async () => { + const id = uuidv4(); + const timestamp = '2020-10-28T06:45:00.000Z'; + + const firstExecutionDocuments = [ + { + host: { name: ['host-a', 'host-b'] }, + id, + '@timestamp': timestamp, + }, + { + host: { name: ['host-a', 'host-b'] }, + id, + '@timestamp': timestamp, + }, + ]; + + await indexListOfDocuments([...firstExecutionDocuments]); + + const rule: EsqlRuleCreateProps = { + ...getCreateEsqlRulesSchemaMock('rule-1', true), + query: getNonAggRuleQueryWithMetadata(id), + alert_suppression: { + group_by: ['host.name'], + missing_fields_strategy: 'suppress', + }, + from: 'now-35m', + interval: '30m', + }; + + const { previewId } = await previewRule({ + supertest, + rule, + timeframeEnd: new Date('2020-10-28T07:00:00.000Z'), + invocationCount: 1, + }); + const previewAlerts = await getPreviewAlerts({ + es, + previewId, + sort: [ALERT_ORIGINAL_TIME], + }); + expect(previewAlerts.length).toEqual(1); + expect(previewAlerts[0]._source).toEqual({ + ...previewAlerts[0]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'host.name', + value: ['host-a', 'host-b'], + }, + ], + [TIMESTAMP]: '2020-10-28T07:00:00.000Z', + [ALERT_LAST_DETECTED]: '2020-10-28T07:00:00.000Z', + [ALERT_ORIGINAL_TIME]: timestamp, + [ALERT_SUPPRESSION_START]: timestamp, + [ALERT_SUPPRESSION_END]: timestamp, + [ALERT_SUPPRESSION_DOCS_COUNT]: 1, + }); + }); + + it('should suppress alerts with missing fields during rule execution only for multiple suppress by fields', async () => { + const id = uuidv4(); + const timestamp = '2020-10-28T06:45:00.000Z'; + + const firstExecutionDocuments = [ + // no missing fields + { + host: { name: 'host-a', ip: '127.0.0.11' }, + agent: { name: 'agent-a', version: 10 }, + id, + '@timestamp': timestamp, + }, + { + host: { name: 'host-a', ip: '127.0.0.12' }, + agent: { name: 'agent-a', version: 10 }, + id, + '@timestamp': timestamp, + }, + // missing agent.name + { + host: { name: 'host-a', ip: '127.0.0.21' }, + agent: { version: 10 }, + id, + '@timestamp': timestamp, + }, + { + host: { name: 'host-a', ip: '127.0.0.22' }, + agent: { version: 10 }, + id, + '@timestamp': timestamp, + }, + // missing agent.version + { + host: { name: 'host-a', ip: '127.0.0.31' }, + agent: { name: 'agent-a' }, + id, + '@timestamp': timestamp, + }, + { + host: { name: 'host-a', ip: '127.0.0.32' }, + agent: { name: 'agent-a' }, + id, + '@timestamp': timestamp, + }, + // missing both agent.* + { + host: { name: 'host-a', ip: '127.0.0.41' }, + id, + '@timestamp': timestamp, + }, + { + host: { name: 'host-a', ip: '127.0.0.42' }, + id, + '@timestamp': timestamp, + }, + ]; + + await indexListOfDocuments(firstExecutionDocuments); + + const rule: EsqlRuleCreateProps = { + ...getCreateEsqlRulesSchemaMock('rule-1', true), + query: getNonAggRuleQueryWithMetadata(id), + alert_suppression: { + group_by: ['agent.name', 'agent.version'], + missing_fields_strategy: 'suppress', + }, + from: 'now-35m', + interval: '30m', + }; + + const { previewId } = await previewRule({ + supertest, + rule, + timeframeEnd: new Date('2020-10-28T07:00:00.000Z'), + invocationCount: 1, + }); + const previewAlerts = await getPreviewAlerts({ + es, + previewId, + sort: ['agent.name', 'agent.version', ALERT_ORIGINAL_TIME], + }); + expect(previewAlerts.length).toEqual(4); + expect(previewAlerts[0]._source).toEqual({ + ...previewAlerts[0]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'agent.name', + value: 'agent-a', + }, + { + field: 'agent.version', + value: '10', + }, + ], + [ALERT_SUPPRESSION_DOCS_COUNT]: 1, + }); + + expect(previewAlerts[1]._source).toEqual({ + ...previewAlerts[1]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'agent.name', + value: 'agent-a', + }, + { + field: 'agent.version', + value: null, + }, + ], + [ALERT_SUPPRESSION_DOCS_COUNT]: 1, + }); + + expect(previewAlerts[2]._source).toEqual({ + ...previewAlerts[2]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'agent.name', + value: null, + }, + { + field: 'agent.version', + value: '10', + }, + ], + [ALERT_SUPPRESSION_DOCS_COUNT]: 1, + }); + + expect(previewAlerts[3]._source).toEqual({ + ...previewAlerts[3]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'agent.name', + value: null, + }, + { + field: 'agent.version', + value: null, + }, + ], + [ALERT_SUPPRESSION_DOCS_COUNT]: 1, + }); + }); + + it('should not suppress alerts with missing fields during rule execution only if configured so for multiple suppress by fields', async () => { + const id = uuidv4(); + const timestamp = '2020-10-28T06:45:00.000Z'; + + const firstExecutionDocuments = [ + // no missing fields + { + host: { name: 'host-a', ip: '127.0.0.11' }, + agent: { name: 'agent-a', version: 10 }, + id, + '@timestamp': timestamp, + }, + { + host: { name: 'host-a', ip: '127.0.0.12' }, + agent: { name: 'agent-a', version: 10 }, + id, + '@timestamp': timestamp, + }, + // missing agent.name + { + host: { name: 'host-a', ip: '127.0.0.21' }, + agent: { version: 10 }, + id, + '@timestamp': timestamp, + }, + { + host: { name: 'host-a', ip: '127.0.0.22' }, + agent: { version: 10 }, + id, + '@timestamp': timestamp, + }, + // missing agent.version + { + host: { name: 'host-a', ip: '127.0.0.31' }, + agent: { name: 'agent-a' }, + id, + '@timestamp': timestamp, + }, + { + host: { name: 'host-a', ip: '127.0.0.32' }, + agent: { name: 'agent-a' }, + id, + '@timestamp': timestamp, + }, + // missing both agent.* + { + host: { name: 'host-a', ip: '127.0.0.41' }, + id, + '@timestamp': timestamp, + }, + { + host: { name: 'host-a', ip: '127.0.0.42' }, + id, + '@timestamp': timestamp, + }, + ]; + + await indexListOfDocuments(firstExecutionDocuments); + + const rule: EsqlRuleCreateProps = { + ...getCreateEsqlRulesSchemaMock('rule-1', true), + query: getNonAggRuleQueryWithMetadata(id), + alert_suppression: { + group_by: ['agent.name', 'agent.version'], + missing_fields_strategy: 'doNotSuppress', + }, + from: 'now-35m', + interval: '30m', + }; + + const { previewId } = await previewRule({ + supertest, + rule, + timeframeEnd: new Date('2020-10-28T07:00:00.000Z'), + invocationCount: 1, + }); + const previewAlerts = await getPreviewAlerts({ + es, + previewId, + sort: ['agent.name', 'agent.version', ALERT_ORIGINAL_TIME], + }); + // from 8 injected, only one should be suppressed + expect(previewAlerts.length).toEqual(7); + expect(previewAlerts[0]._source).toEqual({ + ...previewAlerts[0]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'agent.name', + value: 'agent-a', + }, + { + field: 'agent.version', + value: '10', + }, + ], + [ALERT_SUPPRESSION_DOCS_COUNT]: 1, + }); + + // rest of alerts are not suppressed and do not have suppress properties + previewAlerts.slice(1).forEach((previewAlert) => { + const source = previewAlert._source; + expect(source).toHaveProperty('id', id); + expect(source).not.toHaveProperty(ALERT_SUPPRESSION_DOCS_COUNT); + expect(source).not.toHaveProperty(ALERT_SUPPRESSION_END); + expect(source).not.toHaveProperty(ALERT_SUPPRESSION_TERMS); + expect(source).not.toHaveProperty(ALERT_SUPPRESSION_DOCS_COUNT); + }); + }); + + it('should deduplicate alerts while suppressing new ones on rule execution', async () => { + const id = uuidv4(); + const firstTimestamp = '2020-10-28T05:45:00.000Z'; + const secondTimestamp = '2020-10-28T06:10:00.000Z'; + + const firstExecutionDocuments = [ + { + host: { name: 'host-a' }, + id, + '@timestamp': firstTimestamp, + }, + { + host: { name: 'host-a' }, + id, + '@timestamp': firstTimestamp, + }, + ]; + const secondExecutionDocuments = [ + { + host: { name: 'host-a' }, + id, + '@timestamp': secondTimestamp, + }, + { + host: { name: 'host-a' }, + id, + '@timestamp': secondTimestamp, + }, + { + host: { name: 'host-a' }, + id, + '@timestamp': secondTimestamp, + }, + ]; + + await indexListOfDocuments([...firstExecutionDocuments, ...secondExecutionDocuments]); + + const rule: EsqlRuleCreateProps = { + ...getCreateEsqlRulesSchemaMock('rule-1', true), + query: getNonAggRuleQueryWithMetadata(id), + alert_suppression: { + group_by: ['host.name'], + missing_fields_strategy: 'suppress', + }, + // large look-back time covers all docs + from: 'now-1h', + interval: '30m', + }; + + const { previewId } = await previewRule({ + supertest, + rule, + timeframeEnd: new Date('2020-10-28T06:30:00.000Z'), + invocationCount: 2, + }); + const previewAlerts = await getPreviewAlerts({ + es, + previewId, + sort: ['host.name', ALERT_ORIGINAL_TIME], + }); + expect(previewAlerts.length).toEqual(2); + expect(previewAlerts[0]._source).toEqual({ + ...previewAlerts[0]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'host.name', + value: 'host-a', + }, + ], + [ALERT_ORIGINAL_TIME]: firstTimestamp, + [ALERT_SUPPRESSION_START]: firstTimestamp, + [ALERT_SUPPRESSION_END]: firstTimestamp, + [ALERT_SUPPRESSION_DOCS_COUNT]: 1, + }); + expect(previewAlerts[1]._source).toEqual({ + ...previewAlerts[1]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'host.name', + value: 'host-a', + }, + ], + [ALERT_ORIGINAL_TIME]: secondTimestamp, + [ALERT_SUPPRESSION_START]: secondTimestamp, + [ALERT_SUPPRESSION_END]: secondTimestamp, + [ALERT_SUPPRESSION_DOCS_COUNT]: 2, + }); + }); + + it('should not suppress more than limited number of alerts (max_signals)', async () => { + const id = uuidv4(); + + await indexGeneratedDocuments({ + docsCount: 12000, + seed: (index) => ({ + id, + '@timestamp': `2020-10-28T06:50:00.${index}Z`, + host: { + name: `host-${index}`, + }, + agent: { name: 'agent-a' }, + }), + }); + + const rule: EsqlRuleCreateProps = { + ...getCreateEsqlRulesSchemaMock('rule-1', true), + query: `from ecs_compliant metadata _id, _index, _version ${internalIdPipe( + id + )} | sort @timestamp asc`, + alert_suppression: { + group_by: ['agent.name'], + missing_fields_strategy: 'suppress', + }, + from: 'now-35m', + interval: '30m', + max_signals: 200, + }; + + const { previewId, logs } = await previewRule({ + supertest, + rule, + timeframeEnd: new Date('2020-10-28T07:00:00.000Z'), + invocationCount: 1, + }); + + expect(logs[0].warnings).toEqual( + expect.arrayContaining([getSuppressionMaxAlertsWarning()]) + ); + + const previewAlerts = await getPreviewAlerts({ + es, + previewId, + sort: ['agent.name', ALERT_ORIGINAL_TIME], + size: 1000, + }); + expect(previewAlerts.length).toEqual(1); + expect(previewAlerts[0]._source).toEqual({ + ...previewAlerts[0]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'agent.name', + value: 'agent-a', + }, + ], + [ALERT_SUPPRESSION_DOCS_COUNT]: 200, + }); + }); + + it('should generate up to max_signals alerts', async () => { + const id = uuidv4(); + const timestamp = '2020-10-28T06:05:00.000Z'; + + await indexGeneratedDocuments({ + docsCount: 20000, + seed: (index) => ({ + id, + '@timestamp': timestamp, + host: { + name: `host-${index}`, + }, + 'agent.name': `agent-${index}`, + }), + }); + + const rule: EsqlRuleCreateProps = { + ...getCreateEsqlRulesSchemaMock('rule-1', true), + query: getNonAggRuleQueryWithMetadata(id), + alert_suppression: { + group_by: ['agent.name'], + duration: { + value: 300, + unit: 'm', + }, + missing_fields_strategy: 'suppress', + }, + from: 'now-35m', + interval: '30m', + max_signals: 150, + }; + + const { previewId, logs } = await previewRule({ + supertest, + rule, + timeframeEnd: new Date('2020-10-28T06:30:00.000Z'), + invocationCount: 1, + }); + expect(logs[0].warnings).toEqual( + expect.arrayContaining([getSuppressionMaxAlertsWarning()]) + ); + + const previewAlerts = await getPreviewAlerts({ + es, + previewId, + size: 1000, + sort: ['agent.name', ALERT_ORIGINAL_TIME], + }); + expect(previewAlerts.length).toEqual(150); + }); + }); + + describe('with exceptions', async () => { + afterEach(async () => { + await deleteAllExceptions(supertest, log); + }); + + it('should apply exceptions', async () => { + const id = uuidv4(); + const interval: [string, string] = ['2020-10-28T06:00:00.000Z', '2020-10-28T06:10:00.000Z']; + const doc1 = { agent: { name: 'test-1' }, 'client.ip': '127.0.0.2' }; + const doc2 = { agent: { name: 'test-1' } }; + const doc3 = { agent: { name: 'test-1' }, 'client.ip': '127.0.0.1' }; + + await indexEnhancedDocuments({ documents: [doc1, doc2, doc3], interval, id }); + + const rule: EsqlRuleCreateProps = { + ...getCreateEsqlRulesSchemaMock('rule-1', true), + query: `from ecs_compliant ${internalIdPipe(id)} | where agent.name=="test-1"`, + from: 'now-1h', + interval: '1h', + alert_suppression: { + group_by: ['agent.name'], + missing_fields_strategy: 'suppress', + }, + }; + + const { previewId } = await previewRuleWithExceptionEntries({ + supertest, + log, + rule, + timeframeEnd: new Date('2020-10-28T06:30:00.000Z'), + entries: [ + [ + { + field: 'client.ip', + operator: 'included', + type: 'match', + value: '127.0.0.1', + }, + ], + ], + }); + + const previewAlerts = await getPreviewAlerts({ es, previewId }); + + expect(previewAlerts.length).toBe(1); + expect(previewAlerts[0]._source).toEqual({ + ...previewAlerts[0]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'agent.name', + value: 'test-1', + }, + ], + [ALERT_SUPPRESSION_DOCS_COUNT]: 1, + }); + }); + }); + + describe('alerts enrichment', () => { + before(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/entity/risks'); + }); + + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/entity/risks'); + }); + + it('should be enriched with host risk score', async () => { + const id = uuidv4(); + const interval: [string, string] = ['2020-10-28T06:00:00.000Z', '2020-10-28T06:10:00.000Z']; + const doc1 = { host: { name: 'host-0' } }; + + await indexEnhancedDocuments({ documents: [doc1, doc1], interval, id }); + + const rule: EsqlRuleCreateProps = { + ...getCreateEsqlRulesSchemaMock('rule-1', true), + query: `from ecs_compliant ${internalIdPipe(id)} | where host.name=="host-0"`, + from: 'now-1h', + interval: '1h', + alert_suppression: { + group_by: ['host.name'], + missing_fields_strategy: 'suppress', + }, + }; + + const { previewId } = await previewRule({ + supertest, + rule, + timeframeEnd: new Date('2020-10-28T06:30:00.000Z'), + }); + + const previewAlerts = await getPreviewAlerts({ es, previewId }); + + expect(previewAlerts.length).toBe(1); + + expect(previewAlerts[0]._source).toHaveProperty('host.risk.calculated_level', 'Low'); + expect(previewAlerts[0]._source).toHaveProperty('host.risk.calculated_score_norm', 1); + }); + }); + + describe('with asset criticality', async () => { + before(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/asset_criticality'); + await kibanaServer.uiSettings.update({ + [ENABLE_ASSET_CRITICALITY_SETTING]: true, + }); + }); + + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/asset_criticality'); + }); + + it('should be enriched alert with criticality_level', async () => { + const id = uuidv4(); + const interval: [string, string] = ['2020-10-28T06:00:00.000Z', '2020-10-28T06:10:00.000Z']; + const doc1 = { host: { name: 'host-0' } }; + + await indexEnhancedDocuments({ documents: [doc1, doc1], interval, id }); + + const rule: EsqlRuleCreateProps = { + ...getCreateEsqlRulesSchemaMock('rule-1', true), + query: `from ecs_compliant ${internalIdPipe(id)} | where host.name=="host-0"`, + from: 'now-1h', + interval: '1h', + alert_suppression: { + group_by: ['host.name'], + missing_fields_strategy: 'suppress', + }, + }; + + const { previewId } = await previewRule({ + supertest, + rule, + timeframeEnd: new Date('2020-10-28T06:30:00.000Z'), + }); + + const previewAlerts = await getPreviewAlerts({ es, previewId }); + + expect(previewAlerts.length).toBe(1); + + expect(previewAlerts[0]?._source?.['host.asset.criticality']).toBe('extreme_impact'); + }); + }); + }); +}; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/index.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/index.ts index 23406033ed239..3ea2c4e6c9359 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/index.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/index.ts @@ -12,15 +12,16 @@ export default ({ loadTestFile }: FtrProviderContext): void => { loadTestFile(require.resolve('./eql')); loadTestFile(require.resolve('./eql_alert_suppression')); loadTestFile(require.resolve('./esql')); + loadTestFile(require.resolve('./esql_suppression')); loadTestFile(require.resolve('./machine_learning')); loadTestFile(require.resolve('./new_terms')); loadTestFile(require.resolve('./new_terms_alert_suppression')); loadTestFile(require.resolve('./saved_query')); - loadTestFile(require.resolve('./threat_match')); - loadTestFile(require.resolve('./threat_match_alert_suppression')); + loadTestFile(require.resolve('./indicator_match')); + loadTestFile(require.resolve('./indicator_match_alert_suppression')); loadTestFile(require.resolve('./threshold')); loadTestFile(require.resolve('./threshold_alert_suppression')); loadTestFile(require.resolve('./non_ecs_fields')); - loadTestFile(require.resolve('./query')); + loadTestFile(require.resolve('./custom_query')); }); }; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/threat_match.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/indicator_match.ts similarity index 100% rename from x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/threat_match.ts rename to x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/indicator_match.ts diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/threat_match_alert_suppression.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/indicator_match_alert_suppression.ts similarity index 100% rename from x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/threat_match_alert_suppression.ts rename to x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/indicator_match_alert_suppression.ts diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/utils.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/utils.ts index ec6fac6cf2bcd..31278019dad3e 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/utils.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/utils.ts @@ -10,10 +10,7 @@ import supertest from 'supertest'; import { NodeMetrics } from '@kbn/task-manager-plugin/server/routes/metrics'; import { RetryService } from '@kbn/ftr-common-functional-services'; -export const getMetricsRequest = ( - request: supertest.SuperTest, - reset: boolean = false -) => { +export const getMetricsRequest = (request: supertest.Agent, reset: boolean = false) => { return request .get(`/api/task_manager/metrics${reset ? '' : '?reset=false'}`) .set('kbn-xsrf', 'foo') @@ -22,7 +19,7 @@ export const getMetricsRequest = ( }; export const getMetricsWithRetry = ( - request: supertest.SuperTest, + request: supertest.Agent, retry: RetryService, reset: boolean = false, callback?: (metrics: NodeMetrics) => boolean diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_bulk_actions/trial_license_complete_tier/perform_bulk_action.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_bulk_actions/trial_license_complete_tier/perform_bulk_action.ts index f728b011b6801..22ac52dc2a333 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_bulk_actions/trial_license_complete_tier/perform_bulk_action.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_bulk_actions/trial_license_complete_tier/perform_bulk_action.ts @@ -142,6 +142,10 @@ export default ({ getService }: FtrProviderContext): void => { ], max_signals: 100, setup: '# some setup markdown', + required_fields: [ + { name: '@timestamp', type: 'date' }, + { name: 'my-non-ecs-field', type: 'keyword' }, + ], }; const mockRule = getCustomQueryRuleParams(defaultableFields); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_creation/basic_license_essentials_tier/create_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_creation/basic_license_essentials_tier/create_rules.ts index 63b1a4dfecdc7..925961c5a3720 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_creation/basic_license_essentials_tier/create_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_creation/basic_license_essentials_tier/create_rules.ts @@ -9,8 +9,8 @@ import expect from 'expect'; import { RuleCreateProps } from '@kbn/security-solution-plugin/common/api/detection_engine'; import { - getSimpleRule, getCustomQueryRuleParams, + getSimpleRule, getSimpleRuleOutputWithoutRuleId, getSimpleRuleWithoutRuleId, removeServerGeneratedProperties, @@ -70,7 +70,7 @@ export default ({ getService }: FtrProviderContext) => { }); it('should create a rule with defaultable fields', async () => { - const expectedRule = getCustomQueryRuleParams({ + const ruleCreateProperties = getCustomQueryRuleParams({ rule_id: 'rule-1', max_signals: 200, setup: '# some setup markdown', @@ -78,10 +78,22 @@ export default ({ getService }: FtrProviderContext) => { { package: 'package-a', version: '^1.2.3' }, { package: 'package-b', integration: 'integration-b', version: '~1.1.1' }, ], + required_fields: [ + { name: '@timestamp', type: 'date' }, + { name: 'my-non-ecs-field', type: 'keyword' }, + ], }); + const expectedRule = { + ...ruleCreateProperties, + required_fields: [ + { name: '@timestamp', type: 'date', ecs: true }, + { name: 'my-non-ecs-field', type: 'keyword', ecs: false }, + ], + }; + const { body: createdRuleResponse } = await securitySolutionApi - .createRule({ body: expectedRule }) + .createRule({ body: ruleCreateProperties }) .expect(200); expect(createdRuleResponse).toMatchObject(expectedRule); @@ -207,6 +219,32 @@ export default ({ getService }: FtrProviderContext) => { ); }); }); + + describe('required_fields', () => { + it('creates a rule with required_fields defaulted to an empty array when not present', async () => { + const customQueryRuleParams = getCustomQueryRuleParams({ + rule_id: 'rule-without-required-fields', + }); + + expect(customQueryRuleParams.required_fields).toBeUndefined(); + + const { body } = await securitySolutionApi + .createRule({ + body: customQueryRuleParams, + }) + .expect(200); + + expect(body.required_fields).toEqual([]); + + const { body: createdRule } = await securitySolutionApi + .readRule({ + query: { rule_id: 'rule-without-required-fields' }, + }) + .expect(200); + + expect(createdRule.required_fields).toEqual([]); + }); + }); }); }); }; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_creation/basic_license_essentials_tier/create_rules_bulk.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_creation/basic_license_essentials_tier/create_rules_bulk.ts index dc02f8450f411..4356c8b82b8b4 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_creation/basic_license_essentials_tier/create_rules_bulk.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_creation/basic_license_essentials_tier/create_rules_bulk.ts @@ -69,7 +69,7 @@ export default ({ getService }: FtrProviderContext): void => { }); it('should create a rule with defaultable fields', async () => { - const expectedRule = getCustomQueryRuleParams({ + const ruleCreateProperties = getCustomQueryRuleParams({ rule_id: 'rule-1', max_signals: 200, setup: '# some setup markdown', @@ -77,10 +77,22 @@ export default ({ getService }: FtrProviderContext): void => { { package: 'package-a', version: '^1.2.3' }, { package: 'package-b', integration: 'integration-b', version: '~1.1.1' }, ], + required_fields: [ + { name: '@timestamp', type: 'date' }, + { name: 'my-non-ecs-field', type: 'keyword' }, + ], }); + const expectedRule = { + ...ruleCreateProperties, + required_fields: [ + { name: '@timestamp', type: 'date', ecs: true }, + { name: 'my-non-ecs-field', type: 'keyword', ecs: false }, + ], + }; + const { body: createdRulesBulkResponse } = await securitySolutionApi - .bulkCreateRules({ body: [expectedRule] }) + .bulkCreateRules({ body: [ruleCreateProperties] }) .expect(200); expect(createdRulesBulkResponse[0]).toMatchObject(expectedRule); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/basic_license_essentials_tier/export_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/basic_license_essentials_tier/export_rules.ts index 5986e4d40fe3a..783d0bb42fd87 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/basic_license_essentials_tier/export_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/basic_license_essentials_tier/export_rules.ts @@ -57,9 +57,22 @@ export default ({ getService }: FtrProviderContext): void => { { package: 'package-b', integration: 'integration-b', version: '~1.1.1' }, ], setup: '# some setup markdown', + required_fields: [ + { name: '@timestamp', type: 'date' }, + { name: 'my-non-ecs-field', type: 'keyword' }, + ], }; + const ruleToExport = getCustomQueryRuleParams(defaultableFields); + const expectedRule = { + ...ruleToExport, + required_fields: [ + { name: '@timestamp', type: 'date', ecs: true }, + { name: 'my-non-ecs-field', type: 'keyword', ecs: false }, + ], + }; + await securitySolutionApi.createRule({ body: ruleToExport }); const { body } = await securitySolutionApi @@ -69,7 +82,7 @@ export default ({ getService }: FtrProviderContext): void => { const exportedRule = JSON.parse(body.toString().split(/\n/)[0]); - expect(exportedRule).toMatchObject(defaultableFields); + expect(exportedRule).toMatchObject(expectedRule); }); it('should have export summary reflecting a number of rules', async () => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/basic_license_essentials_tier/import_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/basic_license_essentials_tier/import_rules.ts index 71f40086a29f6..fb02b47067f8e 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/basic_license_essentials_tier/import_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/basic_license_essentials_tier/import_rules.ts @@ -125,11 +125,25 @@ export default ({ getService }: FtrProviderContext): void => { { package: 'package-a', version: '^1.2.3' }, { package: 'package-b', integration: 'integration-b', version: '~1.1.1' }, ], + required_fields: [ + { name: '@timestamp', type: 'date' }, + { name: 'my-non-ecs-field', type: 'keyword' }, + ], }; + const ruleToImport = getCustomQueryRuleParams({ ...defaultableFields, rule_id: 'rule-1', }); + + const expectedRule = { + ...ruleToImport, + required_fields: [ + { name: '@timestamp', type: 'date', ecs: true }, + { name: 'my-non-ecs-field', type: 'keyword', ecs: false }, + ], + }; + const ndjson = combineToNdJson(ruleToImport); await securitySolutionApi @@ -143,7 +157,7 @@ export default ({ getService }: FtrProviderContext): void => { }) .expect(200); - expect(importedRule).toMatchObject(ruleToImport); + expect(importedRule).toMatchObject(expectedRule); }); it('should be able to import two rules', async () => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/basic_license_essentials_tier/patch_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/basic_license_essentials_tier/patch_rules.ts index bdbbc271c26e5..a2658ed2fb285 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/basic_license_essentials_tier/patch_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/basic_license_essentials_tier/patch_rules.ts @@ -61,7 +61,7 @@ export default ({ getService }: FtrProviderContext) => { }); it('should patch defaultable fields', async () => { - const expectedRule = getCustomQueryRuleParams({ + const rulePatchProperties = getCustomQueryRuleParams({ rule_id: 'rule-1', max_signals: 200, setup: '# some setup markdown', @@ -69,8 +69,14 @@ export default ({ getService }: FtrProviderContext) => { { package: 'package-a', version: '^1.2.3' }, { package: 'package-b', integration: 'integration-b', version: '~1.1.1' }, ], + required_fields: [{ name: '@timestamp', type: 'date' }], }); + const expectedRule = { + ...rulePatchProperties, + required_fields: [{ name: '@timestamp', type: 'date', ecs: true }], + }; + await securitySolutionApi.createRule({ body: getCustomQueryRuleParams({ rule_id: 'rule-1' }), }); @@ -78,10 +84,7 @@ export default ({ getService }: FtrProviderContext) => { const { body: patchedRuleResponse } = await securitySolutionApi .patchRule({ body: { - rule_id: 'rule-1', - max_signals: expectedRule.max_signals, - setup: expectedRule.setup, - related_integrations: expectedRule.related_integrations, + ...rulePatchProperties, }, }) .expect(200); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/basic_license_essentials_tier/patch_rules_bulk.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/basic_license_essentials_tier/patch_rules_bulk.ts index 539c39061aa5f..a04245eac5517 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/basic_license_essentials_tier/patch_rules_bulk.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/basic_license_essentials_tier/patch_rules_bulk.ts @@ -60,7 +60,7 @@ export default ({ getService }: FtrProviderContext) => { }); it('should patch defaultable fields', async () => { - const expectedRule = getCustomQueryRuleParams({ + const rulePatchProperties = getCustomQueryRuleParams({ rule_id: 'rule-1', max_signals: 200, setup: '# some setup markdown', @@ -68,8 +68,14 @@ export default ({ getService }: FtrProviderContext) => { { package: 'package-a', version: '^1.2.3' }, { package: 'package-b', integration: 'integration-b', version: '~1.1.1' }, ], + required_fields: [{ name: '@timestamp', type: 'date' }], }); + const expectedRule = { + ...rulePatchProperties, + required_fields: [{ name: '@timestamp', type: 'date', ecs: true }], + }; + await securitySolutionApi.createRule({ body: getCustomQueryRuleParams({ rule_id: 'rule-1' }), }); @@ -78,10 +84,7 @@ export default ({ getService }: FtrProviderContext) => { .bulkPatchRules({ body: [ { - rule_id: 'rule-1', - max_signals: expectedRule.max_signals, - setup: expectedRule.setup, - related_integrations: expectedRule.related_integrations, + ...rulePatchProperties, }, ], }) diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_update/basic_license_essentials_tier/update_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_update/basic_license_essentials_tier/update_rules.ts index ccf598a00da2e..33505f9d150d6 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_update/basic_license_essentials_tier/update_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_update/basic_license_essentials_tier/update_rules.ts @@ -66,7 +66,7 @@ export default ({ getService }: FtrProviderContext) => { }); it('should update a rule with defaultable fields', async () => { - const expectedRule = getCustomQueryRuleParams({ + const ruleUpdateProperties = getCustomQueryRuleParams({ rule_id: 'rule-1', max_signals: 200, setup: '# some setup markdown', @@ -74,15 +74,21 @@ export default ({ getService }: FtrProviderContext) => { { package: 'package-a', version: '^1.2.3' }, { package: 'package-b', integration: 'integration-b', version: '~1.1.1' }, ], + required_fields: [{ name: '@timestamp', type: 'date' }], }); + const expectedRule = { + ...ruleUpdateProperties, + required_fields: [{ name: '@timestamp', type: 'date', ecs: true }], + }; + await securitySolutionApi.createRule({ body: getCustomQueryRuleParams({ rule_id: 'rule-1' }), }); const { body: updatedRuleResponse } = await securitySolutionApi .updateRule({ - body: expectedRule, + body: ruleUpdateProperties, }) .expect(200); @@ -273,6 +279,33 @@ export default ({ getService }: FtrProviderContext) => { ); }); }); + + describe('required_fields', () => { + it('should reset required fields field to default value on update when not present', async () => { + const expectedRule = getCustomQueryRuleParams({ + rule_id: 'required-fields-default-value-test', + required_fields: [], + }); + + await securitySolutionApi.createRule({ + body: getCustomQueryRuleParams({ + rule_id: 'required-fields-default-value-test', + required_fields: [{ name: 'host.name', type: 'keyword' }], + }), + }); + + const { body: updatedRuleResponse } = await securitySolutionApi + .updateRule({ + body: getCustomQueryRuleParams({ + rule_id: 'required-fields-default-value-test', + required_fields: undefined, + }), + }) + .expect(200); + + expect(updatedRuleResponse).toMatchObject(expectedRule); + }); + }); }); }); }; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_update/basic_license_essentials_tier/update_rules_bulk.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_update/basic_license_essentials_tier/update_rules_bulk.ts index fc7c7229ef107..effc64a241cc5 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_update/basic_license_essentials_tier/update_rules_bulk.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_update/basic_license_essentials_tier/update_rules_bulk.ts @@ -65,7 +65,7 @@ export default ({ getService }: FtrProviderContext) => { }); it('should update a rule with defaultable fields', async () => { - const expectedRule = getCustomQueryRuleParams({ + const ruleUpdateProperties = getCustomQueryRuleParams({ rule_id: 'rule-1', max_signals: 200, setup: '# some setup markdown', @@ -73,15 +73,21 @@ export default ({ getService }: FtrProviderContext) => { { package: 'package-a', version: '^1.2.3' }, { package: 'package-b', integration: 'integration-b', version: '~1.1.1' }, ], + required_fields: [{ name: '@timestamp', type: 'date' }], }); + const expectedRule = { + ...ruleUpdateProperties, + required_fields: [{ name: '@timestamp', type: 'date', ecs: true }], + }; + await securitySolutionApi.createRule({ body: getCustomQueryRuleParams({ rule_id: 'rule-1' }), }); const { body: updatedRulesBulkResponse } = await securitySolutionApi .bulkUpdateRules({ - body: [expectedRule], + body: [ruleUpdateProperties], }) .expect(200); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/actions/create_new_webhook_action.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/actions/create_new_webhook_action.ts index 79ea9738372f0..79a5b715f9fe1 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/actions/create_new_webhook_action.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/actions/create_new_webhook_action.ts @@ -17,9 +17,7 @@ import { getWebHookAction } from './get_web_hook_action'; * * @param supertest The supertest deps */ -export const createWebHookRuleAction = async ( - supertest: SuperTest.SuperTest -): Promise => { +export const createWebHookRuleAction = async (supertest: SuperTest.Agent): Promise => { return ( await supertest .post('/api/actions/action') diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/get_alerts.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/get_alerts.ts index 21d8233c8496b..70caaa4edfd2c 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/get_alerts.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/get_alerts.ts @@ -18,7 +18,7 @@ import { refreshIndex } from '..'; import { getAlertsByIds, waitForRuleStatus } from '../../../../../common/utils/security_solution'; export type GetAlerts = ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, es: Client, rule: RuleResponse, diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/migrations/finalize_alerts_migration.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/migrations/finalize_alerts_migration.ts index 02a7475f54aac..1f7d03cab9c5e 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/migrations/finalize_alerts_migration.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/migrations/finalize_alerts_migration.ts @@ -21,7 +21,7 @@ export const finalizeAlertsMigration = async ({ supertest, log, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; log: ToolingLog; migrationIds: string[]; }): Promise => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/migrations/start_alerts_migration.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/migrations/start_alerts_migration.ts index 5d472221154c1..304cf11d79dce 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/migrations/start_alerts_migration.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/migrations/start_alerts_migration.ts @@ -21,7 +21,7 @@ export const startAlertsMigration = async ({ supertest, log, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; log: ToolingLog; indices: string[]; }): Promise => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/wait_for_alert_to_complete.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/wait_for_alert_to_complete.ts index 7d942198bc0d5..d6799e6be611c 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/wait_for_alert_to_complete.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/wait_for_alert_to_complete.ts @@ -11,7 +11,7 @@ import type SuperTest from 'supertest'; import { waitFor } from '../../../../../common/utils/security_solution'; export const waitForAlertToComplete = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, id: string ): Promise => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/connectors/create_connector.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/connectors/create_connector.ts index 0ca8f88166c89..c6c67e37ebf24 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/connectors/create_connector.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/connectors/create_connector.ts @@ -15,7 +15,7 @@ export interface CreateConnectorBody { } export async function createConnector( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, connector: CreateConnectorBody, id = '' ): Promise { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/connectors/delete_connector.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/connectors/delete_connector.ts index 683f845fd8bf8..1003c7cd8880b 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/connectors/delete_connector.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/connectors/delete_connector.ts @@ -7,9 +7,6 @@ import type SuperTest from 'supertest'; -export function deleteConnector( - supertest: SuperTest.SuperTest, - connectorId: string -): SuperTest.Test { +export function deleteConnector(supertest: SuperTest.Agent, connectorId: string): SuperTest.Test { return supertest.delete(`/api/actions/connector/${connectorId}`).set('kbn-xsrf', 'foo'); } diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/connectors/get_connector.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/connectors/get_connector.ts index 8f7e4830372f9..9132b188ecd33 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/connectors/get_connector.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/connectors/get_connector.ts @@ -9,7 +9,7 @@ import { Connector } from '@kbn/actions-plugin/server/application/connector/type import type SuperTest from 'supertest'; export async function getConnector( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, connectorId: string ): Promise { const response = await supertest diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/item/create_exception_list_item.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/item/create_exception_list_item.ts index fccbd3e243b17..39f202c13bce6 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/item/create_exception_list_item.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/item/create_exception_list_item.ts @@ -22,7 +22,7 @@ import { EXCEPTION_LIST_ITEM_URL } from '@kbn/securitysolution-list-constants'; * @param log The tooling logger */ export const createExceptionListItem = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, exceptionListItem: CreateExceptionListItemSchema ): Promise => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/create_container_with_endpoint_entries.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/create_container_with_endpoint_entries.ts index 7541514448b5c..442d341815c2f 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/create_container_with_endpoint_entries.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/create_container_with_endpoint_entries.ts @@ -27,7 +27,7 @@ import { createExceptionList } from './create_exception_list'; * @param osTypes The os types to optionally add or not to add to the container */ export const createContainerWithEndpointEntries = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, endpointEntries: Array<{ entries: NonEmptyEntriesArray; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/create_container_with_entries.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/create_container_with_entries.ts index 973e0d1962a75..36aa1fb0e0652 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/create_container_with_entries.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/create_container_with_entries.ts @@ -23,7 +23,7 @@ import { waitFor } from '../../../../../../common/utils/security_solution'; * @param osTypes The os types to optionally add or not to add to the container */ export const createContainerWithEntries = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, entries: NonEmptyEntriesArray[] ): Promise => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/create_exception_list.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/create_exception_list.ts index 24ebabb5243b2..cae20d074a1c3 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/create_exception_list.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/create_exception_list.ts @@ -23,7 +23,7 @@ import { deleteExceptionList } from './delete_exception_list'; * @param log The tooling logger */ export const createExceptionList = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, exceptionList: CreateExceptionListSchema ): Promise => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/delete_exception_list.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/delete_exception_list.ts index 6c5558a005b97..17cc52ac8c4b6 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/delete_exception_list.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/delete_exception_list.ts @@ -18,7 +18,7 @@ import type { RuleResponse } from '@kbn/security-solution-plugin/common/api/dete * @param log The tooling logger */ export const deleteExceptionList = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, listId: string ): Promise => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/get_stats.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/get_stats.ts index 9415f6900a54f..bd8b3d3ea175c 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/get_stats.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/get_stats.ts @@ -22,7 +22,7 @@ import { getDetectionMetricsFromBody } from './get_detection_metrics_from_body'; * @returns The detection metrics */ export const getStats = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog ): Promise => { const response = await supertest diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/machine_learning/machine_learning_setup.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/machine_learning/machine_learning_setup.ts index d7c7e6387c739..a9b9bf1c8ce5b 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/machine_learning/machine_learning_setup.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/machine_learning/machine_learning_setup.ts @@ -15,7 +15,7 @@ export const executeSetupModuleRequest = async ({ }: { module: string; rspCode: number; - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; }) => { const { body } = await supertest .post(`/internal/ml/modules/setup/${module}`) @@ -40,7 +40,7 @@ export const forceStartDatafeeds = async ({ }: { jobId: string; rspCode: number; - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; }) => { const { body } = await supertest .post(`/internal/ml/jobs/force_start_datafeeds`) diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_legacy_rule_action.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_legacy_rule_action.ts index 8e0cb59d5ee90..439dd6a44f4d8 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_legacy_rule_action.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_legacy_rule_action.ts @@ -10,7 +10,7 @@ import type SuperTest from 'supertest'; import { UPDATE_OR_CREATE_LEGACY_ACTIONS } from '@kbn/security-solution-plugin/common/constants'; export const createLegacyRuleAction = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, alertId: string, connectorId: string ): Promise => diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_non_security_rule.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_non_security_rule.ts index 09bc0f9b81a6d..c6fd0f8d1bc78 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_non_security_rule.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_non_security_rule.ts @@ -30,9 +30,7 @@ const SIMPLE_APM_RULE_DATA = { * Created a non security rule. Helpful in tests to verify functionality works with presence of non security rules. * @param supertest The supertest deps */ -export async function createNonSecurityRule( - supertest: SuperTest.SuperTest -): Promise { +export async function createNonSecurityRule(supertest: SuperTest.Agent): Promise { await supertest .post('/api/alerting/rule') .set('kbn-xsrf', 'true') diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_rule_saved_object.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_rule_saved_object.ts index 93a6322011623..f4e3f22a0c9a6 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_rule_saved_object.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_rule_saved_object.ts @@ -21,7 +21,7 @@ import { * @param supertest */ export const createRuleThroughAlertingEndpoint = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, rule: InternalRuleCreate ): Promise> => { const { body } = await supertest diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_rule_with_auth.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_rule_with_auth.ts index 7b5e4435a3dbb..cde330f8c76a1 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_rule_with_auth.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_rule_with_auth.ts @@ -19,7 +19,7 @@ import type { * @param rule The rule to create */ export const createRuleWithAuth = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, rule: RuleCreateProps, auth: { user: string; pass: string } ): Promise => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_rule_with_exception_entries.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_rule_with_exception_entries.ts index ca2a8129b7713..a04ac5950596e 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_rule_with_exception_entries.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_rule_with_exception_entries.ts @@ -31,7 +31,7 @@ import { createRule } from '../../../../../common/utils/security_solution'; * @param osTypes The os types to optionally add or not to add to the container */ export const createRuleWithExceptionEntries = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, rule: RuleCreateProps, entries: NonEmptyEntriesArray[], diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/fetch_rule.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/fetch_rule.ts index 8e9e1008ff404..651bb2a595f8f 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/fetch_rule.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/fetch_rule.ts @@ -18,7 +18,7 @@ import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common * @param rule The rule to create */ export const fetchRule = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, idOrRuleId: { id: string; ruleId?: never } | { id?: never; ruleId: string } ): Promise => ( diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/find_immutable_rule_by_id.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/find_immutable_rule_by_id.ts index 55e7375c48986..f4b026748e986 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/find_immutable_rule_by_id.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/find_immutable_rule_by_id.ts @@ -17,7 +17,7 @@ import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common * @param supertest The supertest deps */ export const findImmutableRuleById = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, ruleId: string ): Promise<{ diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_coverage_overview.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_coverage_overview.ts index f93a29b0ec149..b512494f245e2 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_coverage_overview.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_coverage_overview.ts @@ -13,7 +13,7 @@ import { } from '@kbn/security-solution-plugin/common/api/detection_engine'; export const getCoverageOverview = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, filter?: CoverageOverviewFilter ): Promise => { const response = await supertest diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_rule_actions.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_rule_actions.ts index 3251d2b0a6b56..36784cc47aa1a 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_rule_actions.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_rule_actions.ts @@ -11,19 +11,16 @@ import { RuleActionArray } from '@kbn/securitysolution-io-ts-alerting-types'; import { getSlackAction } from '..'; import { getWebHookAction } from '..'; -const createConnector = async ( - supertest: SuperTest.SuperTest, - payload: Record -) => +const createConnector = async (supertest: SuperTest.Agent, payload: Record) => (await supertest.post('/api/actions/action').set('kbn-xsrf', 'true').send(payload).expect(200)) .body; -const createWebHookConnector = (supertest: SuperTest.SuperTest) => +const createWebHookConnector = (supertest: SuperTest.Agent) => createConnector(supertest, getWebHookAction()); -const createSlackConnector = (supertest: SuperTest.SuperTest) => +const createSlackConnector = (supertest: SuperTest.Agent) => createConnector(supertest, getSlackAction()); export const getActionsWithoutFrequencies = async ( - supertest: SuperTest.SuperTest + supertest: SuperTest.Agent ): Promise => { const webHookAction = await createWebHookConnector(supertest); const slackConnector = await createSlackConnector(supertest); @@ -44,7 +41,7 @@ export const getActionsWithoutFrequencies = async ( }; export const getActionsWithFrequencies = async ( - supertest: SuperTest.SuperTest + supertest: SuperTest.Agent ): Promise => { const webHookAction = await createWebHookConnector(supertest); const slackConnector = await createSlackConnector(supertest); @@ -67,7 +64,7 @@ export const getActionsWithFrequencies = async ( }; export const getSomeActionsWithFrequencies = async ( - supertest: SuperTest.SuperTest + supertest: SuperTest.Agent ): Promise => { const webHookAction = await createWebHookConnector(supertest); const slackConnector = await createSlackConnector(supertest); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/patch_rule.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/patch_rule.ts index f62f49b20d622..44456c93a16cd 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/patch_rule.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/patch_rule.ts @@ -21,7 +21,7 @@ import { * @param rule The rule to create */ export const patchRule = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, patchedRule: RulePatchProps ): Promise => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/delete_prebuilt_rules_fleet_package.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/delete_prebuilt_rules_fleet_package.ts index 1647ff301a324..a3468ccb32b5a 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/delete_prebuilt_rules_fleet_package.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/delete_prebuilt_rules_fleet_package.ts @@ -13,9 +13,7 @@ import type SuperTest from 'supertest'; * * @param supertest Supertest instance */ -export async function deletePrebuiltRulesFleetPackage( - supertest: SuperTest.SuperTest -) { +export async function deletePrebuiltRulesFleetPackage(supertest: SuperTest.Agent) { const resp = await supertest .get(epmRouteService.getInfoPath('security_detection_engine')) .set('kbn-xsrf', 'true') diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_installed_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_installed_rules.ts index f796dd06e777b..04a9c52565bf1 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_installed_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_installed_rules.ts @@ -17,9 +17,7 @@ import { FindRulesResponse } from '@kbn/security-solution-plugin/common/api/dete * @returns Fleet install package response */ -export const getInstalledRules = async ( - supertest: SuperTest.SuperTest -): Promise => { +export const getInstalledRules = async (supertest: SuperTest.Agent): Promise => { const { body: rulesResponse } = await supertest .get(`${DETECTION_ENGINE_RULES_URL_FIND}?per_page=10000`) .set('kbn-xsrf', 'true') diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_prebuilt_rules_and_timelines_status.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_prebuilt_rules_and_timelines_status.ts index 7f683ca9994be..420f8a7aca1ce 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_prebuilt_rules_and_timelines_status.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_prebuilt_rules_and_timelines_status.ts @@ -21,7 +21,7 @@ import { refreshSavedObjectIndices } from '../../refresh_index'; */ export const getPrebuiltRulesAndTimelinesStatus = async ( es: Client, - supertest: SuperTest.SuperTest + supertest: SuperTest.Agent ): Promise => { await refreshSavedObjectIndices(es); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_prebuilt_rules_fleet_package.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_prebuilt_rules_fleet_package.ts index ec69b6cfb14c2..2441e6f4dbc88 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_prebuilt_rules_fleet_package.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_prebuilt_rules_fleet_package.ts @@ -14,7 +14,7 @@ import type SuperTest from 'supertest'; * @param supertest Supertest instance * @returns The API endpoint response. Will have status 200 if package installed or 404 if not */ -export async function getPrebuiltRulesFleetPackage(supertest: SuperTest.SuperTest) { +export async function getPrebuiltRulesFleetPackage(supertest: SuperTest.Agent) { return await supertest .get(epmRouteService.getInfoPath('security_detection_engine')) .set('kbn-xsrf', 'true') diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_prebuilt_rules_status.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_prebuilt_rules_status.ts index da044637fc77b..10ca202c66f46 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_prebuilt_rules_status.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_prebuilt_rules_status.ts @@ -20,7 +20,7 @@ import { refreshSavedObjectIndices } from '../../refresh_index'; */ export const getPrebuiltRulesStatus = async ( es: Client, - supertest: SuperTest.SuperTest + supertest: SuperTest.Agent ): Promise => { await refreshSavedObjectIndices(es); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_fleet_package_by_url.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_fleet_package_by_url.ts index 863e4d79fb006..b88a848758a8f 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_fleet_package_by_url.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_fleet_package_by_url.ts @@ -26,7 +26,7 @@ const ATTEMPT_TIMEOUT = 120000; export const installPrebuiltRulesPackageViaFleetAPI = async ( es: Client, - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, retryService: RetryService ): Promise => { const fleetResponse = await retryService.tryWithRetries( @@ -66,7 +66,7 @@ export const installPrebuiltRulesPackageViaFleetAPI = async ( export const installPrebuiltRulesPackageByVersion = async ( es: Client, - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, version: string, retryService: RetryService ): Promise => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_mock_prebuilt_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_mock_prebuilt_rules.ts index 0e15f416e1238..843d0531e53ba 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_mock_prebuilt_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_mock_prebuilt_rules.ts @@ -19,7 +19,7 @@ import { installPrebuiltRulesAndTimelines } from './install_prebuilt_rules_and_t * @returns Install prebuilt rules response */ export const installMockPrebuiltRules = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, es: Client ): Promise => { // Ensure there are prebuilt rule saved objects before installing rules diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_prebuilt_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_prebuilt_rules.ts index 499f97877bf16..eec88072e7d1e 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_prebuilt_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_prebuilt_rules.ts @@ -30,7 +30,7 @@ import { refreshSavedObjectIndices } from '../../refresh_index'; */ export const installPrebuiltRules = async ( es: Client, - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, rules?: RuleVersionSpecifier[] ): Promise => { let payload = {}; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_prebuilt_rules_and_timelines.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_prebuilt_rules_and_timelines.ts index c83e8693f2390..a52a44a90bfe7 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_prebuilt_rules_and_timelines.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_prebuilt_rules_and_timelines.ts @@ -31,7 +31,7 @@ import { refreshSavedObjectIndices } from '../../refresh_index'; */ export const installPrebuiltRulesAndTimelines = async ( es: Client, - supertest: SuperTest.SuperTest + supertest: SuperTest.Agent ): Promise => { const response = await supertest .put(PREBUILT_RULES_URL) diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_prebuilt_rules_fleet_package.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_prebuilt_rules_fleet_package.ts index cbe609501a5f2..f7a7337d40241 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_prebuilt_rules_fleet_package.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_prebuilt_rules_fleet_package.ts @@ -36,7 +36,7 @@ export const installPrebuiltRulesFleetPackage = async ({ retryService, }: { es: Client; - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; version?: string; overrideExistingPackage: boolean; retryService: RetryService; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/review_install_prebuilt_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/review_install_prebuilt_rules.ts index 573b481a3b30f..487d2dbe53044 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/review_install_prebuilt_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/review_install_prebuilt_rules.ts @@ -16,7 +16,7 @@ import type SuperTest from 'supertest'; * @returns Review Install prebuilt rules response */ export const reviewPrebuiltRulesToInstall = async ( - supertest: SuperTest.SuperTest + supertest: SuperTest.Agent ): Promise => { const response = await supertest .post(REVIEW_RULE_INSTALLATION_URL) diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/review_upgrade_prebuilt_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/review_upgrade_prebuilt_rules.ts index 9bbf980dcccca..17347ffcdd1e3 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/review_upgrade_prebuilt_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/review_upgrade_prebuilt_rules.ts @@ -16,7 +16,7 @@ import type SuperTest from 'supertest'; * @returns Review Upgrade prebuilt rules response */ export const reviewPrebuiltRulesToUpgrade = async ( - supertest: SuperTest.SuperTest + supertest: SuperTest.Agent ): Promise => { const response = await supertest .post(REVIEW_RULE_UPGRADE_URL) diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/upgrade_prebuilt_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/upgrade_prebuilt_rules.ts index c22aa9106a272..f12d0adbc65f3 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/upgrade_prebuilt_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/upgrade_prebuilt_rules.ts @@ -26,7 +26,7 @@ import { refreshSavedObjectIndices } from '../../refresh_index'; */ export const upgradePrebuiltRules = async ( es: Client, - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, rules?: RuleVersionSpecifier[] ): Promise => { let payload = {}; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/preview_rule.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/preview_rule.ts index ec060c4076404..a939454e72c9f 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/preview_rule.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/preview_rule.ts @@ -27,7 +27,7 @@ export const previewRule = async ({ invocationCount = 1, timeframeEnd = new Date(), }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; rule: RuleCreateProps; invocationCount?: number; timeframeEnd?: Date; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/preview_rule_with_exception_entries.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/preview_rule_with_exception_entries.ts index fb5d480bbbffc..f02a5d370daaa 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/preview_rule_with_exception_entries.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/preview_rule_with_exception_entries.ts @@ -35,7 +35,7 @@ export const previewRuleWithExceptionEntries = async ({ invocationCount, timeframeEnd, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; log: ToolingLog; rule: RuleCreateProps; entries: NonEmptyEntriesArray[]; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/update_rule.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/update_rule.ts index 5c6c69a230465..cee439311d2a1 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/update_rule.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/update_rule.ts @@ -21,7 +21,7 @@ import { * @param rule The rule to create */ export const updateRule = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, updatedRule: RuleUpdateProps ): Promise => ( diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/telemetry/get_security_telemetry_stats.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/telemetry/get_security_telemetry_stats.ts index 462cdecbb498d..d1345597adc95 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/telemetry/get_security_telemetry_stats.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/telemetry/get_security_telemetry_stats.ts @@ -21,7 +21,7 @@ import { * @returns The detection metrics */ export const getSecurityTelemetryStats = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog ): Promise => { const response = await supertest diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_score_preview.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_score_preview.ts index 98ea7b172583c..28ebe8dae5f56 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_score_preview.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_score_preview.ts @@ -514,56 +514,6 @@ export default ({ getService }: FtrProviderContext): void => { }); }); - context('with category weights', () => { - it('weights risk inputs from different categories according to the category weight', async () => { - const documentId = uuidv4(); - const userSignal = buildDocument( - { 'event.kind': 'signal', 'user.name': 'user-1' }, - documentId - ); - const hostSignal = buildDocument( - { 'event.kind': 'signal', 'host.name': 'host-1' }, - documentId - ); - await indexListOfDocuments(Array(50).fill(userSignal).concat(Array(50).fill(hostSignal))); - - await createAndSyncRuleAndAlerts({ - query: `id: ${documentId}`, - alerts: 100, - riskScore: 100, - }); - const { scores } = await previewRiskScores({ - body: { - weights: [{ type: 'risk_category', value: 'category_1', host: 0.4, user: 0.8 }], - }, - }); - - expect(sanitizeScores(scores.host!)).to.eql([ - { - calculated_level: 'Low', - calculated_score: 93.2375911647125, - calculated_score_norm: 35.695861854790394, - category_1_score: 35.69586185479039, - category_1_count: 50, - id_field: 'host.name', - id_value: 'host-1', - }, - ]); - - expect(sanitizeScores(scores.user!)).to.eql([ - { - calculated_level: 'High', - calculated_score: 186.475182329425, - calculated_score_norm: 71.39172370958079, - category_1_score: 71.39172370958077, - category_1_count: 50, - id_field: 'user.name', - id_value: 'user-1', - }, - ]); - }); - }); - describe('@skipInServerless with asset criticality data', () => { const assetCriticalityRoutes = assetCriticalityRouteHelpersFactory(supertest); diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/asset_criticality.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/asset_criticality.ts index 6f13a84504e1d..3a46aa56ef614 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/asset_criticality.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/asset_criticality.ts @@ -104,7 +104,7 @@ export const getAssetCriticalityDoc = async (opts: { }; export const assetCriticalityRouteHelpersFactory = ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, namespace?: string ) => ({ status: async () => @@ -170,7 +170,7 @@ export const assetCriticalityRouteHelpersFactory = ( }); export const assetCriticalityRouteHelpersFactoryNoAuth = ( - supertestWithoutAuth: SuperTest.SuperTest, + supertestWithoutAuth: SuperTest.Agent, namespace?: string ) => ({ privilegesForUser: async ({ username, password }: { username: string; password: string }) => diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/get_risk_engine_stats.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/get_risk_engine_stats.ts index 5629e0da9a89d..fd9ff0eb88177 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/get_risk_engine_stats.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/get_risk_engine_stats.ts @@ -22,7 +22,7 @@ import { getRiskEngineMetricsFromBody } from './get_risk_engine_metrics_from_bod * @returns The detection metrics */ export const getRiskEngineStats = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog ): Promise => { const response = await supertest diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/risk_engine.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/risk_engine.ts index be7010ab1fc7b..dbd4ed78c1896 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/risk_engine.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/risk_engine.ts @@ -76,7 +76,7 @@ export const createAndSyncRuleAndAlertsFactory = log, namespace, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; log: ToolingLog; namespace?: string; }) => @@ -412,7 +412,7 @@ export const clearLegacyDashboards = async ({ supertest, log, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; log: ToolingLog; }): Promise => { try { @@ -481,10 +481,7 @@ export const getLegacyRiskScoreDashboards = async ({ return savedObejectLens?.saved_objects.filter((s) => s?.attributes?.title?.includes('Risk')); }; -export const riskEngineRouteHelpersFactory = ( - supertest: SuperTest.SuperTest, - namespace?: string -) => ({ +export const riskEngineRouteHelpersFactory = (supertest: SuperTest.Agent, namespace?: string) => ({ init: async (expectStatusCode: number = 200) => await supertest .post(routeWithNamespace(RISK_ENGINE_INIT_URL, namespace)) @@ -536,7 +533,7 @@ interface Credentials { } export const riskEngineRouteHelpersFactoryNoAuth = ( - supertestWithoutAuth: SuperTest.SuperTest, + supertestWithoutAuth: SuperTest.Agent, namespace?: string ) => ({ privilegesForUser: async ({ username, password }: Credentials) => @@ -576,11 +573,7 @@ export const riskEngineRouteHelpersFactoryNoAuth = ( .expect(expectStatusCode), }); -export const installLegacyRiskScore = async ({ - supertest, -}: { - supertest: SuperTest.SuperTest; -}) => { +export const installLegacyRiskScore = async ({ supertest }: { supertest: SuperTest.Agent }) => { await supertest .post('/internal/risk_score') .set('kbn-xsrf', 'true') diff --git a/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/utils/create_connector.ts b/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/utils/create_connector.ts index ac2d60ad631e2..0c7823ef07885 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/utils/create_connector.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/utils/create_connector.ts @@ -45,7 +45,7 @@ const connectorSetup = { * @param spaceId The space id */ export const createConnector = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, objectRemover: ObjectRemover, apiUrl: string, connectorType: 'bedrock' | 'openai', diff --git a/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/utils/post_actions_client_execute.ts b/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/utils/post_actions_client_execute.ts index 0477401a26533..b5ed8c15b3fb2 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/utils/post_actions_client_execute.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/utils/post_actions_client_execute.ts @@ -21,7 +21,7 @@ import { Response } from 'superagent'; export const postActionsClientExecute = async ( connectorId: string, args: any, - supertest: SuperTest.SuperTest + supertest: SuperTest.Agent ): Promise => { const response = await supertest .post(`/internal/elastic_assistant/actions/connector/${connectorId}/_execute`) diff --git a/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/trial_license_complete_tier/helpers.ts b/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/trial_license_complete_tier/helpers.ts index 38212020ba18a..1f1a4bb821082 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/trial_license_complete_tier/helpers.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/trial_license_complete_tier/helpers.ts @@ -9,10 +9,7 @@ import type SuperTest from 'supertest'; import { v4 as uuidv4 } from 'uuid'; import { TimelineType } from '@kbn/security-solution-plugin/common/api/timeline'; -export const createBasicTimeline = async ( - supertest: SuperTest.SuperTest, - titleToSaved: string -) => +export const createBasicTimeline = async (supertest: SuperTest.Agent, titleToSaved: string) => await supertest .post('/api/timeline') .set('kbn-xsrf', 'true') @@ -25,7 +22,7 @@ export const createBasicTimeline = async ( }); export const createBasicTimelineTemplate = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, titleToSaved: string ) => await supertest diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/utils.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/utils.ts index d45a77be0840b..c4bde53fcab7d 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/utils.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/utils.ts @@ -41,7 +41,7 @@ import { countDownTest } from '../../../common/utils/security_solution'; * @param supertest The supertest client library */ export const createListsIndex = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog ): Promise => { return countDownTest( @@ -61,7 +61,7 @@ export const createListsIndex = async ( * @param supertest The supertest client library */ export const deleteListsIndex = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog ): Promise => { return countDownTest( @@ -82,7 +82,7 @@ export const deleteListsIndex = async ( * @param supertest The supertest client library */ export const createExceptionListsIndex = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog ): Promise => { return countDownTest( @@ -205,7 +205,7 @@ export const binaryToString = (res: any, callback: any): void => { * @param supertest The supertest handle */ export const deleteAllExceptions = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog ): Promise => { await deleteAllExceptionsByType(supertest, log, 'single'); @@ -218,7 +218,7 @@ export const deleteAllExceptions = async ( * @param supertest The supertest handle */ export const deleteAllExceptionsByType = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, type: NamespaceType ): Promise => { @@ -260,7 +260,7 @@ export const deleteAllExceptionsByType = async ( * @param testValues Optional test values in case you're using CIDR or range based lists */ export const importFile = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, type: Type, contents: string[], @@ -297,7 +297,7 @@ export const importFile = async ( * @param fileName filename to import as */ export const importTextFile = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, type: Type, contents: string[], @@ -330,7 +330,7 @@ export const importTextFile = async ( * @param itemValue The item value to wait for */ export const waitForListItem = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, itemValue: string, fileName: string @@ -362,7 +362,7 @@ export const waitForListItem = async ( * @param itemValue The item value to wait for */ export const waitForListItems = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, itemValues: string[], fileName: string @@ -378,7 +378,7 @@ export const waitForListItems = async ( * @param itemValue The item value to wait for */ export const waitForTextListItem = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, itemValue: string, fileName: string @@ -417,7 +417,7 @@ export const waitForTextListItem = async ( * @param itemValue The item value to wait for */ export const waitForTextListItems = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, itemValues: string[], fileName: string diff --git a/x-pack/test/security_solution_cypress/config.ts b/x-pack/test/security_solution_cypress/config.ts index 588843b731f45..606df49c4f90e 100644 --- a/x-pack/test/security_solution_cypress/config.ts +++ b/x-pack/test/security_solution_cypress/config.ts @@ -45,6 +45,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { '--xpack.alerting.rules.minimumScheduleInterval.value=1s', '--xpack.ruleRegistry.unsafe.legacyMultiTenancy.enabled=true', `--xpack.securitySolution.enableExperimental=${JSON.stringify([ + 'alertSuppressionForEsqlRuleEnabled', 'bulkCustomHighlightedFieldsEnabled', ])}`, // mock cloud to enable the guided onboarding tour in e2e tests diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/detection_alerts/assignments/assignments.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/detection_alerts/assignments/assignments.cy.ts index 2b28c6d0c8885..d1e800f13672a 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/detection_alerts/assignments/assignments.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/detection_alerts/assignments/assignments.cy.ts @@ -40,7 +40,8 @@ import { } from '../../../../../tasks/alert_assignments'; import { ALERTS_COUNT } from '../../../../../screens/alerts'; -describe('Alert user assignment - ESS & Serverless', { tags: ['@ess', '@serverless'] }, () => { +// FLAKY: https://github.com/elastic/kibana/issues/183787 +describe.skip('Alert user assignment - ESS & Serverless', { tags: ['@ess', '@serverless'] }, () => { before(() => { cy.task('esArchiverLoad', { archiveName: 'auditbeat_multiple' }); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/common_flows.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/common_flows.cy.ts index 6bf3ea611dc7f..abf8bc3934e21 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/common_flows.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/common_flows.cy.ts @@ -33,6 +33,8 @@ import { fillMaxSignals, fillNote, fillReferenceUrls, + // fillRelatedIntegrations, + // fillRequiredFields, fillRiskScore, fillRuleName, fillRuleTags, @@ -67,9 +69,13 @@ describe('Common rule creation flows', { tags: ['@ess', '@serverless'] }, () => it('Creates and enables a rule', function () { cy.log('Filling define section'); importSavedQuery(this.timelineId); - // The following step is flaky due to a recent EUI upgrade. - // Underlying EUI issue: https://github.com/elastic/eui/issues/7761 - // Issue to uncomment this once EUI fix is in place: https://github.com/elastic/kibana/issues/183485 + /* + The following steps are flaky due to a recent EUI upgrade. + + Underlying EUI issue: https://github.com/elastic/eui/issues/7761 + Issue to uncomment these once the EUI fix is in place: https://github.com/elastic/kibana/issues/183485 + */ + // fillRequiredFields(); // fillRelatedIntegrations(); cy.get(DEFINE_CONTINUE_BUTTON).click(); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/common_flows_suppression_serverless_essentials.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/common_flows_suppression_serverless_essentials.cy.ts index 62a406cd9d466..d6f23687cf418 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/common_flows_suppression_serverless_essentials.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/common_flows_suppression_serverless_essentials.cy.ts @@ -9,6 +9,7 @@ import { selectThresholdRuleType, selectIndicatorMatchType, selectNewTermsRuleType, + selectEsqlRuleType, } from '../../../../tasks/create_new_rule'; import { login } from '../../../../tasks/login'; import { visit } from '../../../../tasks/navigation'; @@ -21,6 +22,7 @@ import { CREATE_RULE_URL } from '../../../../urls/navigation'; describe( 'Detection rules, Alert Suppression for Essentials tier', { + // skipped in MKI as it depends on feature flag alertSuppressionForEsqlRuleEnabled tags: ['@serverless', '@skipInServerlessMKI'], env: { ftrConfig: { @@ -29,6 +31,12 @@ describe( { product_line: 'endpoint', product_tier: 'essentials' }, ], }, + // alertSuppressionForEsqlRuleEnabled feature flag is also enabled in a global config + kbnServerArgs: [ + `--xpack.securitySolution.enableExperimental=${JSON.stringify([ + 'alertSuppressionForEsqlRuleEnabled', + ])}`, + ], }, }, () => { @@ -49,6 +57,9 @@ describe( selectThresholdRuleType(); cy.get(THRESHOLD_ENABLE_SUPPRESSION_CHECKBOX).should('be.enabled'); + + selectEsqlRuleType(); + cy.get(ALERT_SUPPRESSION_FIELDS_INPUT).should('be.enabled'); }); } ); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/common_flows_supression_ess_basic.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/common_flows_supression_ess_basic.cy.ts index fbcc43e4652ae..1f86d6d0dd789 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/common_flows_supression_ess_basic.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/common_flows_supression_ess_basic.cy.ts @@ -14,6 +14,7 @@ import { selectIndicatorMatchType, selectNewTermsRuleType, selectThresholdRuleType, + selectEsqlRuleType, openSuppressionFieldsTooltipAndCheckLicense, } from '../../../../tasks/create_new_rule'; import { startBasicLicense } from '../../../../tasks/api_calls/licensing'; @@ -48,6 +49,9 @@ describe( selectNewTermsRuleType(); openSuppressionFieldsTooltipAndCheckLicense(); + selectEsqlRuleType(); + openSuppressionFieldsTooltipAndCheckLicense(); + selectThresholdRuleType(); cy.get(THRESHOLD_ENABLE_SUPPRESSION_CHECKBOX).should('be.disabled'); cy.get(THRESHOLD_ENABLE_SUPPRESSION_CHECKBOX).parent().trigger('mouseover'); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/event_correlation_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule.cy.ts similarity index 100% rename from x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/event_correlation_rule.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule.cy.ts diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/event_correlation_rule_suppression.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule_suppression.cy.ts similarity index 100% rename from x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/event_correlation_rule_suppression.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule_suppression.cy.ts diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/event_correlation_rule_suppression_ess_basic.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule_suppression_ess_basic.cy.ts similarity index 100% rename from x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/event_correlation_rule_suppression_ess_basic.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule_suppression_ess_basic.cy.ts diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/event_correlation_rule_suppression_sequence.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule_suppression_sequence.cy.ts similarity index 100% rename from x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/event_correlation_rule_suppression_sequence.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule_suppression_sequence.cy.ts diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/event_correlation_rule_suppression_serverless_essentials.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule_suppression_serverless_essentials.cy.ts similarity index 100% rename from x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/event_correlation_rule_suppression_serverless_essentials.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule_suppression_serverless_essentials.cy.ts diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/esql_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/esql_rule.cy.ts new file mode 100644 index 0000000000000..b8cebae392d38 --- /dev/null +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/esql_rule.cy.ts @@ -0,0 +1,292 @@ +/* + * Copyright 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 { getEsqlRule } from '../../../../objects/rule'; + +import { + RULES_MANAGEMENT_TABLE, + RULE_NAME, + INVESTIGATION_FIELDS_VALUE_ITEM, +} from '../../../../screens/alerts_detection_rules'; +import { + RULE_NAME_HEADER, + RULE_TYPE_DETAILS, + RULE_NAME_OVERRIDE_DETAILS, + DEFINITION_DETAILS, + SUPPRESS_BY_DETAILS, + SUPPRESS_FOR_DETAILS, + SUPPRESS_MISSING_FIELD, +} from '../../../../screens/rule_details'; + +import { ESQL_QUERY_BAR } from '../../../../screens/create_new_rule'; + +import { getDetails, goBackToRulesTable } from '../../../../tasks/rule_details'; +import { expectNumberOfRules } from '../../../../tasks/alerts_detection_rules'; +import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; +import { + expandEsqlQueryBar, + fillAboutRuleAndContinue, + fillDefineEsqlRuleAndContinue, + fillScheduleRuleAndContinue, + selectEsqlRuleType, + getDefineContinueButton, + fillEsqlQueryBar, + fillAboutSpecificEsqlRuleAndContinue, + createRuleWithoutEnabling, + expandAdvancedSettings, + fillCustomInvestigationFields, + fillRuleName, + fillDescription, + getAboutContinueButton, + fillAlertSuppressionFields, + selectAlertSuppressionPerInterval, + setAlertSuppressionDuration, + selectDoNotSuppressForMissingFields, + continueFromDefineStep, + fillAboutRuleMinimumAndContinue, + skipScheduleRuleAction, + interceptEsqlQueryFieldsRequest, +} from '../../../../tasks/create_new_rule'; +import { login } from '../../../../tasks/login'; +import { visit } from '../../../../tasks/navigation'; + +import { CREATE_RULE_URL } from '../../../../urls/navigation'; + +// https://github.com/cypress-io/cypress/issues/22113 +// issue is inside monaco editor, used in ES|QL query input +// calling it after visiting page in each tests, seems fixes the issue +// the only other alternative is patching ResizeObserver, which is something I would like to avoid +const workaroundForResizeObserver = () => + cy.on('uncaught:exception', (err) => { + if (err.message.includes('ResizeObserver loop limit exceeded')) { + return false; + } + }); + +describe( + 'Detection ES|QL rules, creation', + { + // skipped in MKI as it depends on feature flag alertSuppressionForEsqlRuleEnabled + // alertSuppressionForEsqlRuleEnabled feature flag is also enabled in a global config + tags: ['@ess', '@serverless', '@skipInServerlessMKI'], + env: { + kbnServerArgs: [ + `--xpack.securitySolution.enableExperimental=${JSON.stringify([ + 'alertSuppressionForEsqlRuleEnabled', + ])}`, + ], + }, + }, + () => { + const rule = getEsqlRule(); + const expectedNumberOfRules = 1; + + describe('creation', () => { + beforeEach(() => { + deleteAlertsAndRules(); + login(); + + visit(CREATE_RULE_URL); + workaroundForResizeObserver(); + }); + + it('creates an ES|QL rule', function () { + selectEsqlRuleType(); + expandEsqlQueryBar(); + + fillDefineEsqlRuleAndContinue(rule); + fillAboutRuleAndContinue(rule); + fillScheduleRuleAndContinue(rule); + createRuleWithoutEnabling(); + + // ensures after rule save ES|QL rule is displayed + cy.get(RULE_NAME_HEADER).should('contain', `${rule.name}`); + getDetails(RULE_TYPE_DETAILS).contains('ES|QL'); + + // ensures newly created rule is displayed in table + goBackToRulesTable(); + + expectNumberOfRules(RULES_MANAGEMENT_TABLE, expectedNumberOfRules); + + cy.get(RULE_NAME).should('have.text', rule.name); + }); + + // this test case is important, since field shown in rule override component are coming from ES|QL query, not data view fields API + it('creates an ES|QL rule and overrides its name', function () { + selectEsqlRuleType(); + expandEsqlQueryBar(); + + fillDefineEsqlRuleAndContinue(rule); + fillAboutSpecificEsqlRuleAndContinue({ ...rule, rule_name_override: 'test_id' }); + fillScheduleRuleAndContinue(rule); + createRuleWithoutEnabling(); + + // ensure rule name override is displayed on details page + getDetails(RULE_NAME_OVERRIDE_DETAILS).should('have.text', 'test_id'); + }); + }); + + describe('ES|QL query validation', () => { + beforeEach(() => { + login(); + visit(CREATE_RULE_URL); + + workaroundForResizeObserver(); + }); + it('shows error when ES|QL query is empty', function () { + selectEsqlRuleType(); + expandEsqlQueryBar(); + getDefineContinueButton().click(); + + cy.get(ESQL_QUERY_BAR).contains('ES|QL query is required'); + }); + + it('proceeds further once invalid query is fixed', function () { + selectEsqlRuleType(); + expandEsqlQueryBar(); + getDefineContinueButton().click(); + + cy.get(ESQL_QUERY_BAR).contains('required'); + + // once correct query typed, we can proceed ot the next step + fillEsqlQueryBar(rule.query); + getDefineContinueButton().click(); + + cy.get(ESQL_QUERY_BAR).should('not.be.visible'); + }); + + it('shows error when non-aggregating ES|QL query does not have metadata operator', function () { + const invalidNonAggregatingQuery = 'from auditbeat* | limit 5'; + selectEsqlRuleType(); + expandEsqlQueryBar(); + fillEsqlQueryBar(invalidNonAggregatingQuery); + getDefineContinueButton().click(); + + cy.get(ESQL_QUERY_BAR).contains( + 'must include the "metadata _id, _version, _index" operator after the source command' + ); + }); + + it('shows error when non-aggregating ES|QL query does not return _id field', function () { + const invalidNonAggregatingQuery = + 'from auditbeat* metadata _id, _version, _index | keep agent.* | limit 5'; + + selectEsqlRuleType(); + expandEsqlQueryBar(); + fillEsqlQueryBar(invalidNonAggregatingQuery); + getDefineContinueButton().click(); + + cy.get(ESQL_QUERY_BAR).contains( + 'must include the "metadata _id, _version, _index" operator after the source command' + ); + }); + + it('shows error when ES|QL query is invalid', function () { + const invalidEsqlQuery = + 'from auditbeat* metadata _id, _version, _index | not_existing_operator'; + visit(CREATE_RULE_URL); + + selectEsqlRuleType(); + expandEsqlQueryBar(); + fillEsqlQueryBar(invalidEsqlQuery); + getDefineContinueButton().click(); + + cy.get(ESQL_QUERY_BAR).contains('Error validating ES|QL'); + }); + }); + + describe('ES|QL investigation fields', () => { + beforeEach(() => { + login(); + visit(CREATE_RULE_URL); + }); + it('shows custom ES|QL field in investigation fields autocomplete and saves it in rule', function () { + const CUSTOM_ESQL_FIELD = '_custom_agent_name'; + const queryWithCustomFields = [ + `from auditbeat* metadata _id, _version, _index`, + `eval ${CUSTOM_ESQL_FIELD} = agent.name`, + `keep _id, _custom_agent_name`, + `limit 5`, + ].join(' | '); + + workaroundForResizeObserver(); + + selectEsqlRuleType(); + expandEsqlQueryBar(); + fillEsqlQueryBar(queryWithCustomFields); + getDefineContinueButton().click(); + + expandAdvancedSettings(); + fillRuleName(); + fillDescription(); + fillCustomInvestigationFields([CUSTOM_ESQL_FIELD]); + getAboutContinueButton().click(); + + fillScheduleRuleAndContinue(rule); + createRuleWithoutEnabling(); + + cy.get(INVESTIGATION_FIELDS_VALUE_ITEM).should('have.text', CUSTOM_ESQL_FIELD); + }); + }); + + describe('Alert suppression', () => { + beforeEach(() => { + login(); + visit(CREATE_RULE_URL); + }); + it('shows custom ES|QL field in investigation fields autocomplete and saves it in rule', function () { + const CUSTOM_ESQL_FIELD = '_custom_agent_name'; + const SUPPRESS_BY_FIELDS = [CUSTOM_ESQL_FIELD, 'agent.type']; + + const queryWithCustomFields = [ + `from auditbeat* metadata _id, _version, _index`, + `eval ${CUSTOM_ESQL_FIELD} = agent.name`, + `drop agent.*`, + ].join(' | '); + + workaroundForResizeObserver(); + + selectEsqlRuleType(); + expandEsqlQueryBar(); + + interceptEsqlQueryFieldsRequest(queryWithCustomFields, 'esqlSuppressionFieldsRequest'); + fillEsqlQueryBar(queryWithCustomFields); + + cy.wait('@esqlSuppressionFieldsRequest'); + fillAlertSuppressionFields(SUPPRESS_BY_FIELDS); + selectAlertSuppressionPerInterval(); + setAlertSuppressionDuration(2, 'h'); + selectDoNotSuppressForMissingFields(); + continueFromDefineStep(); + + // ensures details preview works correctly + cy.get(DEFINITION_DETAILS).within(() => { + getDetails(SUPPRESS_BY_DETAILS).should('have.text', SUPPRESS_BY_FIELDS.join('')); + getDetails(SUPPRESS_FOR_DETAILS).should('have.text', '2h'); + getDetails(SUPPRESS_MISSING_FIELD).should( + 'have.text', + 'Do not suppress alerts for events with missing fields' + ); + }); + + fillAboutRuleMinimumAndContinue(rule); + skipScheduleRuleAction(); + createRuleWithoutEnabling(); + + // ensures rule details displayed correctly after rule created + cy.get(DEFINITION_DETAILS).within(() => { + getDetails(SUPPRESS_BY_DETAILS).should('have.text', SUPPRESS_BY_FIELDS.join('')); + getDetails(SUPPRESS_FOR_DETAILS).should('have.text', '2h'); + getDetails(SUPPRESS_MISSING_FIELD).should( + 'have.text', + 'Do not suppress alerts for events with missing fields' + ); + }); + }); + }); + } +); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/esql_rule_ess.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/esql_rule_ess.cy.ts deleted file mode 100644 index 2e95bb19a0477..0000000000000 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/esql_rule_ess.cy.ts +++ /dev/null @@ -1,219 +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 { getEsqlRule } from '../../../../objects/rule'; - -import { - RULES_MANAGEMENT_TABLE, - RULE_NAME, - INVESTIGATION_FIELDS_VALUE_ITEM, -} from '../../../../screens/alerts_detection_rules'; -import { - RULE_NAME_HEADER, - RULE_TYPE_DETAILS, - RULE_NAME_OVERRIDE_DETAILS, -} from '../../../../screens/rule_details'; - -import { ESQL_QUERY_BAR } from '../../../../screens/create_new_rule'; - -import { getDetails, goBackToRulesTable } from '../../../../tasks/rule_details'; -import { expectNumberOfRules } from '../../../../tasks/alerts_detection_rules'; -import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; -import { - expandEsqlQueryBar, - fillAboutRuleAndContinue, - fillDefineEsqlRuleAndContinue, - fillScheduleRuleAndContinue, - selectEsqlRuleType, - getDefineContinueButton, - fillEsqlQueryBar, - fillAboutSpecificEsqlRuleAndContinue, - createRuleWithoutEnabling, - expandAdvancedSettings, - fillCustomInvestigationFields, - fillRuleName, - fillDescription, - getAboutContinueButton, -} from '../../../../tasks/create_new_rule'; -import { login } from '../../../../tasks/login'; -import { visit } from '../../../../tasks/navigation'; - -import { CREATE_RULE_URL } from '../../../../urls/navigation'; - -// https://github.com/cypress-io/cypress/issues/22113 -// issue is inside monaco editor, used in ES|QL query input -// calling it after visiting page in each tests, seems fixes the issue -// the only other alternative is patching ResizeObserver, which is something I would like to avoid -const workaroundForResizeObserver = () => - cy.on('uncaught:exception', (err) => { - if (err.message.includes('ResizeObserver loop limit exceeded')) { - return false; - } - }); - -describe('Detection ES|QL rules, creation', { tags: ['@ess'] }, () => { - const rule = getEsqlRule(); - const expectedNumberOfRules = 1; - - describe('creation', () => { - beforeEach(() => { - deleteAlertsAndRules(); - login(); - }); - - it('creates an ES|QL rule', function () { - visit(CREATE_RULE_URL); - workaroundForResizeObserver(); - - selectEsqlRuleType(); - expandEsqlQueryBar(); - - fillDefineEsqlRuleAndContinue(rule); - fillAboutRuleAndContinue(rule); - fillScheduleRuleAndContinue(rule); - createRuleWithoutEnabling(); - - // ensures after rule save ES|QL rule is displayed - cy.get(RULE_NAME_HEADER).should('contain', `${rule.name}`); - getDetails(RULE_TYPE_DETAILS).contains('ES|QL'); - - // ensures newly created rule is displayed in table - goBackToRulesTable(); - - expectNumberOfRules(RULES_MANAGEMENT_TABLE, expectedNumberOfRules); - - cy.get(RULE_NAME).should('have.text', rule.name); - }); - - // this test case is important, since field shown in rule override component are coming from ES|QL query, not data view fields API - it('creates an ES|QL rule and overrides its name', function () { - visit(CREATE_RULE_URL); - workaroundForResizeObserver(); - - selectEsqlRuleType(); - expandEsqlQueryBar(); - - fillDefineEsqlRuleAndContinue(rule); - fillAboutSpecificEsqlRuleAndContinue({ ...rule, rule_name_override: 'test_id' }); - fillScheduleRuleAndContinue(rule); - createRuleWithoutEnabling(); - - // ensure rule name override is displayed on details page - getDetails(RULE_NAME_OVERRIDE_DETAILS).should('have.text', 'test_id'); - }); - }); - - describe('ES|QL query validation', () => { - beforeEach(() => { - login(); - visit(CREATE_RULE_URL); - }); - it('shows error when ES|QL query is empty', function () { - workaroundForResizeObserver(); - - selectEsqlRuleType(); - expandEsqlQueryBar(); - getDefineContinueButton().click(); - - cy.get(ESQL_QUERY_BAR).contains('ES|QL query is required'); - }); - - it('proceeds further once invalid query is fixed', function () { - workaroundForResizeObserver(); - - selectEsqlRuleType(); - expandEsqlQueryBar(); - getDefineContinueButton().click(); - - cy.get(ESQL_QUERY_BAR).contains('required'); - - // once correct query typed, we can proceed ot the next step - fillEsqlQueryBar(rule.query); - getDefineContinueButton().click(); - - cy.get(ESQL_QUERY_BAR).should('not.be.visible'); - }); - - it('shows error when non-aggregating ES|QL query does not have metadata operator', function () { - workaroundForResizeObserver(); - - const invalidNonAggregatingQuery = 'from auditbeat* | limit 5'; - selectEsqlRuleType(); - expandEsqlQueryBar(); - fillEsqlQueryBar(invalidNonAggregatingQuery); - getDefineContinueButton().click(); - - cy.get(ESQL_QUERY_BAR).contains( - 'must include the "metadata _id, _version, _index" operator after the source command' - ); - }); - - it('shows error when non-aggregating ES|QL query does not return _id field', function () { - workaroundForResizeObserver(); - - const invalidNonAggregatingQuery = - 'from auditbeat* metadata _id, _version, _index | keep agent.* | limit 5'; - - selectEsqlRuleType(); - expandEsqlQueryBar(); - fillEsqlQueryBar(invalidNonAggregatingQuery); - getDefineContinueButton().click(); - - cy.get(ESQL_QUERY_BAR).contains( - 'must include the "metadata _id, _version, _index" operator after the source command' - ); - }); - - it('shows error when ES|QL query is invalid', function () { - workaroundForResizeObserver(); - const invalidEsqlQuery = - 'from auditbeat* metadata _id, _version, _index | not_existing_operator'; - visit(CREATE_RULE_URL); - - selectEsqlRuleType(); - expandEsqlQueryBar(); - fillEsqlQueryBar(invalidEsqlQuery); - getDefineContinueButton().click(); - - cy.get(ESQL_QUERY_BAR).contains('Error validating ES|QL'); - }); - }); - - describe('ES|QL investigation fields', () => { - beforeEach(() => { - login(); - visit(CREATE_RULE_URL); - }); - it('shows custom ES|QL field in investigation fields autocomplete and saves it in rule', function () { - const CUSTOM_ESQL_FIELD = '_custom_agent_name'; - const queryWithCustomFields = [ - `from auditbeat* metadata _id, _version, _index`, - `eval ${CUSTOM_ESQL_FIELD} = agent.name`, - `keep _id, _custom_agent_name`, - `limit 5`, - ].join(' | '); - - workaroundForResizeObserver(); - - selectEsqlRuleType(); - expandEsqlQueryBar(); - fillEsqlQueryBar(queryWithCustomFields); - getDefineContinueButton().click(); - - expandAdvancedSettings(); - fillRuleName(); - fillDescription(); - fillCustomInvestigationFields([CUSTOM_ESQL_FIELD]); - getAboutContinueButton().click(); - - fillScheduleRuleAndContinue(rule); - createRuleWithoutEnabling(); - - cy.get(INVESTIGATION_FIELDS_VALUE_ITEM).should('have.text', CUSTOM_ESQL_FIELD); - }); - }); -}); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_edit/esql_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_edit/esql_rule.cy.ts index 265263ba495c6..a255ce289b1a7 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_edit/esql_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_edit/esql_rule.cy.ts @@ -7,9 +7,22 @@ import { getEsqlRule } from '../../../../objects/rule'; -import { ESQL_QUERY_DETAILS, RULE_NAME_OVERRIDE_DETAILS } from '../../../../screens/rule_details'; +import { + ESQL_QUERY_DETAILS, + RULE_NAME_OVERRIDE_DETAILS, + SUPPRESS_FOR_DETAILS, + DEFINITION_DETAILS, + SUPPRESS_MISSING_FIELD, + SUPPRESS_BY_DETAILS, + DETAILS_TITLE, +} from '../../../../screens/rule_details'; -import { ESQL_QUERY_BAR } from '../../../../screens/create_new_rule'; +import { + ESQL_QUERY_BAR, + ALERT_SUPPRESSION_DURATION_INPUT, + ALERT_SUPPRESSION_FIELDS, + ALERT_SUPPRESSION_MISSING_FIELDS_SUPPRESS, +} from '../../../../screens/create_new_rule'; import { createRule } from '../../../../tasks/api_calls/rules'; @@ -17,12 +30,15 @@ import { RULES_MANAGEMENT_URL } from '../../../../urls/rules_management'; import { getDetails } from '../../../../tasks/rule_details'; import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; import { - clearEsqlQueryBar, expandEsqlQueryBar, fillEsqlQueryBar, fillOverrideEsqlRuleName, goToAboutStepTab, expandAdvancedSettings, + selectAlertSuppressionPerRuleExecution, + selectDoNotSuppressForMissingFields, + fillAlertSuppressionFields, + interceptEsqlQueryFieldsRequest, } from '../../../../tasks/create_new_rule'; import { login } from '../../../../tasks/login'; @@ -33,63 +49,160 @@ import { visit } from '../../../../tasks/navigation'; const rule = getEsqlRule(); -const expectedValidEsqlQuery = 'from auditbeat* | stats count(event.category) by event.category'; - -describe('Detection ES|QL rules, edit', { tags: ['@ess'] }, () => { - beforeEach(() => { - login(); - deleteAlertsAndRules(); - createRule(rule); - }); - - it('edits ES|QL rule and checks details page', () => { - visit(RULES_MANAGEMENT_URL); - editFirstRule(); - expandEsqlQueryBar(); - // ensure once edit form opened, correct query is displayed in ES|QL input - cy.get(ESQL_QUERY_BAR).contains(rule.query); - - clearEsqlQueryBar(); - fillEsqlQueryBar(expectedValidEsqlQuery); - - saveEditedRule(); - - // ensure updated query is displayed on details page - getDetails(ESQL_QUERY_DETAILS).should('have.text', expectedValidEsqlQuery); - }); - - it('edits ES|QL rule query and override rule name with new property', () => { - visit(RULES_MANAGEMENT_URL); - editFirstRule(); - clearEsqlQueryBar(); - fillEsqlQueryBar(expectedValidEsqlQuery); - - goToAboutStepTab(); - expandAdvancedSettings(); - fillOverrideEsqlRuleName('event.category'); - - saveEditedRule(); - - // ensure rule name override is displayed on details page - getDetails(RULE_NAME_OVERRIDE_DETAILS).should('have.text', 'event.category'); - }); - - it('adds ES|QL override rule name on edit', () => { - visit(RULES_MANAGEMENT_URL); - editFirstRule(); - - expandEsqlQueryBar(); - // ensure once edit form opened, correct query is displayed in ES|QL input - cy.get(ESQL_QUERY_BAR).contains(rule.query); - - goToAboutStepTab(); - expandAdvancedSettings(); - // this field defined to be returned in rule query - fillOverrideEsqlRuleName('test_id'); - - saveEditedRule(); - - // ensure rule name override is displayed on details page - getDetails(RULE_NAME_OVERRIDE_DETAILS).should('have.text', 'test_id'); - }); -}); +const expectedValidEsqlQuery = + 'from auditbeat* | stats _count=count(event.category) by event.category'; + +// skipped in MKI as it depends on feature flag alertSuppressionForEsqlRuleEnabled +// alertSuppressionForEsqlRuleEnabled feature flag is also enabled in a global config +describe( + 'Detection ES|QL rules, edit', + { + tags: ['@ess', '@serverless', '@skipInServerlessMKI'], + env: { + kbnServerArgs: [ + `--xpack.securitySolution.enableExperimental=${JSON.stringify([ + 'alertSuppressionForEsqlRuleEnabled', + ])}`, + ], + }, + }, + () => { + beforeEach(() => { + login(); + deleteAlertsAndRules(); + createRule(rule); + + visit(RULES_MANAGEMENT_URL); + editFirstRule(); + }); + + it('edits ES|QL rule and checks details page', () => { + expandEsqlQueryBar(); + // ensure once edit form opened, correct query is displayed in ES|QL input + cy.get(ESQL_QUERY_BAR).contains(rule.query); + + fillEsqlQueryBar(expectedValidEsqlQuery); + + saveEditedRule(); + + // ensure updated query is displayed on details page + getDetails(ESQL_QUERY_DETAILS).should('have.text', expectedValidEsqlQuery); + }); + + it('edits ES|QL rule query and override rule name with new property', () => { + fillEsqlQueryBar(expectedValidEsqlQuery); + + goToAboutStepTab(); + expandAdvancedSettings(); + fillOverrideEsqlRuleName('event.category'); + + saveEditedRule(); + + // ensure rule name override is displayed on details page + getDetails(RULE_NAME_OVERRIDE_DETAILS).should('have.text', 'event.category'); + }); + + it('adds ES|QL override rule name on edit', () => { + expandEsqlQueryBar(); + // ensure once edit form opened, correct query is displayed in ES|QL input + cy.get(ESQL_QUERY_BAR).contains(rule.query); + + goToAboutStepTab(); + expandAdvancedSettings(); + // this field defined to be returned in rule query + fillOverrideEsqlRuleName('test_id'); + + saveEditedRule(); + + // ensure rule name override is displayed on details page + getDetails(RULE_NAME_OVERRIDE_DETAILS).should('have.text', 'test_id'); + }); + + describe('with configured suppression', () => { + const SUPPRESS_BY_FIELDS = ['event.category']; + const NEW_SUPPRESS_BY_FIELDS = ['event.category', '_count']; + + beforeEach(() => { + deleteAlertsAndRules(); + createRule({ + ...rule, + query: expectedValidEsqlQuery, + alert_suppression: { + group_by: SUPPRESS_BY_FIELDS, + duration: { value: 3, unit: 'h' }, + missing_fields_strategy: 'suppress', + }, + }); + }); + + it('displays suppress options correctly on edit form and allows its editing', () => { + visit(RULES_MANAGEMENT_URL); + + interceptEsqlQueryFieldsRequest(expectedValidEsqlQuery, 'esqlSuppressionFieldsRequest'); + editFirstRule(); + + // check saved suppression settings + cy.get(ALERT_SUPPRESSION_DURATION_INPUT).eq(0).should('be.enabled').should('have.value', 3); + cy.get(ALERT_SUPPRESSION_DURATION_INPUT) + .eq(1) + .should('be.enabled') + .should('have.value', 'h'); + cy.get(ALERT_SUPPRESSION_FIELDS).should('contain', SUPPRESS_BY_FIELDS.join('')); + cy.get(ALERT_SUPPRESSION_MISSING_FIELDS_SUPPRESS).should('be.checked'); + + selectAlertSuppressionPerRuleExecution(); + selectDoNotSuppressForMissingFields(); + + cy.wait('@esqlSuppressionFieldsRequest'); + fillAlertSuppressionFields(['_count']); + + saveEditedRule(); + + cy.get(DEFINITION_DETAILS).within(() => { + getDetails(SUPPRESS_BY_DETAILS).should('have.text', NEW_SUPPRESS_BY_FIELDS.join('')); + getDetails(SUPPRESS_FOR_DETAILS).should('have.text', 'One rule execution'); + getDetails(SUPPRESS_MISSING_FIELD).should( + 'have.text', + 'Do not suppress alerts for events with missing fields' + ); + }); + }); + }); + + describe('without suppression', () => { + const SUPPRESS_BY_FIELDS = ['event.category']; + + beforeEach(() => { + deleteAlertsAndRules(); + createRule({ + ...rule, + query: expectedValidEsqlQuery, + }); + }); + + it('enables suppression on time interval', () => { + visit(RULES_MANAGEMENT_URL); + + interceptEsqlQueryFieldsRequest(expectedValidEsqlQuery, 'esqlSuppressionFieldsRequest'); + editFirstRule(); + + cy.wait('@esqlSuppressionFieldsRequest'); + fillAlertSuppressionFields(SUPPRESS_BY_FIELDS); + + saveEditedRule(); + + cy.get(DEFINITION_DETAILS).within(() => { + getDetails(SUPPRESS_BY_DETAILS).should('have.text', SUPPRESS_BY_FIELDS.join('')); + getDetails(SUPPRESS_FOR_DETAILS).should('have.text', 'One rule execution'); + getDetails(SUPPRESS_MISSING_FIELD).should( + 'have.text', + 'Suppress and group alerts for events with missing fields' + ); + + // suppression functionality should be under Tech Preview + cy.contains(DETAILS_TITLE, SUPPRESS_FOR_DETAILS).contains('Technical Preview'); + }); + }); + }); + } +); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/value_lists/value_list_items.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/value_lists/value_list_items.cy.ts index 9e9a6ec8fdf61..11fb0aa197450 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/value_lists/value_list_items.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/value_lists/value_list_items.cy.ts @@ -41,7 +41,8 @@ import { import { RULES_MANAGEMENT_URL } from '../../../../urls/rules_management'; import { getDefaultUsername } from '../../../../tasks/common/users'; -describe( +// Failing: See https://github.com/elastic/kibana/issues/183713 +describe.skip( 'Value list items', { tags: ['@ess', '@serverless'], diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/prebuilt_rules_preview.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/prebuilt_rules_preview.cy.ts index 84198ccd702dd..13af60594d103 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/prebuilt_rules_preview.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/prebuilt_rules_preview.cy.ts @@ -137,8 +137,8 @@ describe('Detection rules, Prebuilt Rules Installation and Update workflow', () { package: 'windows', version: '^1.5.0' }, ], required_fields: [ - { ecs: true, name: 'event.type', type: 'keyword' }, - { ecs: true, name: 'file.extension', type: 'keyword' }, + { name: 'event.type', type: 'keyword' }, + { name: 'file.extension', type: 'keyword' }, ], timeline_id: '3e827bab-838a-469f-bd1e-5e19a2bff2fd', timeline_title: 'Alerts Involving a Single User Timeline', @@ -337,6 +337,14 @@ describe('Detection rules, Prebuilt Rules Installation and Update workflow', () type: 'esql', language: 'esql', query: 'FROM .alerts-security.alerts-default | STATS count = COUNT(@timestamp) BY @timestamp', + alert_suppression: { + group_by: [ + 'Endpoint.policy.applied.artifacts.global.identifiers.name', + 'Endpoint.policy.applied.id', + ], + duration: { unit: 'm', value: 5 }, + missing_fields_strategy: 'suppress', + }, }); const RULE_WITHOUT_INVESTIGATION_AND_SETUP_GUIDES = createRuleAssetSavedObject({ @@ -621,25 +629,23 @@ describe('Detection rules, Prebuilt Rules Installation and Update workflow', () const { query } = NEW_TERMS_INDEX_PATTERN_RULE['security-rule'] as { query: string }; assertCustomQueryPropertyShown(query); }); - }); - describe( - 'Skip in Serverless environment', - { tags: TEST_ENV_TAGS.filter((tag) => tag !== '@serverless') }, - () => { - /* Serverless environment doesn't support ESQL rules just yet */ - it('ESQL rule properties', () => { - clickAddElasticRulesButton(); + it('ESQL rule properties', () => { + clickAddElasticRulesButton(); - openRuleInstallPreview(ESQL_RULE['security-rule'].name); + openRuleInstallPreview(ESQL_RULE['security-rule'].name); - assertCommonPropertiesShown(commonProperties); + assertCommonPropertiesShown(commonProperties); - const { query } = ESQL_RULE['security-rule'] as { query: string }; - assertEsqlQueryPropertyShown(query); - }); - } - ); + const { query } = ESQL_RULE['security-rule'] as { query: string }; + assertEsqlQueryPropertyShown(query); + + const { alert_suppression: alertSuppression } = ESQL_RULE['security-rule'] as { + alert_suppression: AlertSuppression; + }; + assertAlertSuppressionPropertiesShown(alertSuppression); + }); + }); }); }); @@ -1049,26 +1055,24 @@ describe('Detection rules, Prebuilt Rules Installation and Update workflow', () }; assertCustomQueryPropertyShown(query); }); - }); - describe( - 'Skip in Serverless environment', - { tags: TEST_ENV_TAGS.filter((tag) => tag !== '@serverless') }, - () => { - /* Serverless environment doesn't support ESQL rules just yet */ - it('ESQL rule properties', () => { - clickRuleUpdatesTab(); + it('ESQL rule properties', () => { + clickRuleUpdatesTab(); + + openRuleUpdatePreview(UPDATED_ESQL_RULE['security-rule'].name); + selectPreviewTab(PREVIEW_TABS.OVERVIEW); - openRuleUpdatePreview(UPDATED_ESQL_RULE['security-rule'].name); - selectPreviewTab(PREVIEW_TABS.OVERVIEW); + assertCommonPropertiesShown(commonProperties); - assertCommonPropertiesShown(commonProperties); + const { query } = UPDATED_ESQL_RULE['security-rule'] as { query: string }; + assertEsqlQueryPropertyShown(query); - const { query } = UPDATED_ESQL_RULE['security-rule'] as { query: string }; - assertEsqlQueryPropertyShown(query); - }); - } - ); + const { alert_suppression: alertSuppression } = UPDATED_ESQL_RULE['security-rule'] as { + alert_suppression: AlertSuppression; + }; + assertAlertSuppressionPropertiesShown(alertSuppression); + }); + }); }); describe('Viewing rule changes in JSON diff view', { tags: TEST_ENV_TAGS }, () => { diff --git a/x-pack/test/security_solution_cypress/cypress/objects/rule.ts b/x-pack/test/security_solution_cypress/cypress/objects/rule.ts index 0b9a4ddedf04d..fcbaaca3d1dde 100644 --- a/x-pack/test/security_solution_cypress/cypress/objects/rule.ts +++ b/x-pack/test/security_solution_cypress/cypress/objects/rule.ts @@ -406,7 +406,7 @@ export const getEsqlRule = ( ): EsqlRuleCreateProps => ({ type: 'esql', language: 'esql', - query: 'from auditbeat-* [metadata _id, _version, _index] | keep agent.*,_id | eval test_id=_id', + query: 'from auditbeat-* metadata _id, _version, _index | keep agent.*,_id | eval test_id=_id', name: 'ES|QL Rule', description: 'The new rule description.', severity: 'high', diff --git a/x-pack/test/security_solution_cypress/cypress/screens/create_new_rule.ts b/x-pack/test/security_solution_cypress/cypress/screens/create_new_rule.ts index d8feace6af1d0..b0af7275ae808 100644 --- a/x-pack/test/security_solution_cypress/cypress/screens/create_new_rule.ts +++ b/x-pack/test/security_solution_cypress/cypress/screens/create_new_rule.ts @@ -130,6 +130,9 @@ export const IMPORT_QUERY_FROM_SAVED_TIMELINE_LINK = export const RELATED_INTEGRATION_COMBO_BOX_INPUT = '[data-test-subj="relatedIntegrationComboBox"] [data-test-subj="comboBoxSearchInput"]'; +export const REQUIRED_FIELD_COMBO_BOX_INPUT = + '[data-test-subj^="requiredFieldNameSelect"] [data-test-subj="comboBoxSearchInput"]'; + export const INDICATOR_MATCH_TYPE = '[data-test-subj="threatMatchRuleType"]'; export const INPUT = '[data-test-subj="input"]'; diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/create_new_rule.ts b/x-pack/test/security_solution_cypress/cypress/tasks/create_new_rule.ts index 7ee1811760480..181f5dfa22eb1 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/create_new_rule.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/create_new_rule.ts @@ -82,6 +82,7 @@ import { MITRE_TACTIC, QUERY_BAR, REFERENCE_URLS_INPUT, + REQUIRED_FIELD_COMBO_BOX_INPUT, RISK_MAPPING_OVERRIDE_OPTION, RISK_OVERRIDE, RULE_DESCRIPTION_INPUT, @@ -480,6 +481,18 @@ export const fillScheduleRuleAndContinue = (rule: RuleCreateProps) => { cy.get(SCHEDULE_CONTINUE_BUTTON).click({ force: true }); }; +export const fillRequiredFields = (): void => { + addRequiredField(); + addRequiredField(); +}; + +const addRequiredField = (): void => { + cy.contains('button', 'Add required field').should('be.enabled').click(); + + cy.get(REQUIRED_FIELD_COMBO_BOX_INPUT).last().should('be.enabled').click(); + cy.get(COMBO_BOX_OPTION).first().click(); +}; + /** * use default schedule options */ @@ -600,16 +613,23 @@ export const fillDefineNewTermsRuleAndContinue = (rule: NewTermsRuleCreateProps) cy.get(DEFINE_CONTINUE_BUTTON).should('exist').click({ force: true }); }; -export const fillEsqlQueryBar = (query: string) => { +const typeEsqlQueryBar = (query: string) => { // eslint-disable-next-line cypress/no-force cy.get(ESQL_QUERY_BAR_INPUT_AREA).should('not.be.disabled').type(query, { force: true }); }; -export const clearEsqlQueryBar = () => { - // monaco editor under the hood is quite complex in matter to clear it - // underlying textarea holds just the last character of query displayed in search bar - // in order to clear it - it requires to select all text within editor and type in it - fillEsqlQueryBar(Cypress.platform === 'darwin' ? '{cmd}a' : '{ctrl}a'); +/** + * clears ES|QL search bar first + * types new query + */ +export const fillEsqlQueryBar = (query: string) => { + // before typing anything in query bar, we need to clear it + // Since first click on ES|QL query bar trigger re-render. We need to clear search bar during second attempt + typeEsqlQueryBar(' '); + typeEsqlQueryBar(Cypress.platform === 'darwin' ? '{cmd}a{del}' : '{ctrl}a{del}'); + + // only after this query can be safely typed + typeEsqlQueryBar(query); }; /** @@ -926,6 +946,20 @@ export const openSuppressionFieldsTooltipAndCheckLicense = () => { cy.get(TOOLTIP).contains('Platinum license'); }; +/** + * intercepts /internal/bsearch request that contains esqlQuery and adds alias to it + */ +export const interceptEsqlQueryFieldsRequest = ( + esqlQuery: string, + alias: string = 'esqlQueryFields' +) => { + cy.intercept('POST', '/internal/bsearch?*', (req) => { + if (req.body?.batch?.[0]?.request?.params?.query?.includes?.(esqlQuery)) { + req.alias = alias; + } + }); +}; + export const checkLoadQueryDynamically = () => { cy.get(LOAD_QUERY_DYNAMICALLY_CHECKBOX).click({ force: true }); cy.get(LOAD_QUERY_DYNAMICALLY_CHECKBOX).should('be.checked'); diff --git a/x-pack/test/security_solution_cypress/serverless_config.ts b/x-pack/test/security_solution_cypress/serverless_config.ts index 51462be717fc8..04a15e49d070a 100644 --- a/x-pack/test/security_solution_cypress/serverless_config.ts +++ b/x-pack/test/security_solution_cypress/serverless_config.ts @@ -35,6 +35,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { { product_line: 'cloud', product_tier: 'complete' }, ])}`, `--xpack.securitySolution.enableExperimental=${JSON.stringify([ + 'alertSuppressionForEsqlRuleEnabled', 'bulkCustomHighlightedFieldsEnabled', ])}`, ], diff --git a/x-pack/test/security_solution_endpoint/apps/integrations/artifact_entries_list.ts b/x-pack/test/security_solution_endpoint/apps/integrations/artifact_entries_list.ts index 58ffde0d7611f..6c78dd995673f 100644 --- a/x-pack/test/security_solution_endpoint/apps/integrations/artifact_entries_list.ts +++ b/x-pack/test/security_solution_endpoint/apps/integrations/artifact_entries_list.ts @@ -30,7 +30,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const pageObjects = getPageObjects(['common', 'artifactEntriesList']); const testSubjects = getService('testSubjects'); const browser = getService('browser'); - const endpointArtifactsTestResources = getService('endpointArtifactTestResources'); + const endpointArtifactTestResources = getService('endpointArtifactTestResources'); const endpointTestResources = getService('endpointTestResources'); const retry = getService('retry'); const esClient = getService('es'); @@ -76,7 +76,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { // Check edited artifact is in the list with new values (wait for list to be updated) let updatedArtifact: ArtifactElasticsearchProperties | undefined; await retry.waitForWithTimeout('fleet artifact is updated', 120_000, async () => { - const artifacts = await endpointArtifactsTestResources.getArtifacts(); + const artifacts = await endpointArtifactTestResources.getArtifacts(); const manifestArtifact = artifacts.find((artifact) => { return ( diff --git a/x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/artifact_entries_list.ts b/x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/artifact_entries_list.ts new file mode 100644 index 0000000000000..46e333e10779d --- /dev/null +++ b/x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/artifact_entries_list.ts @@ -0,0 +1,376 @@ +/* + * Copyright 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 { unzip } from 'zlib'; +import { promisify } from 'util'; +import expect from '@kbn/expect'; +import { IndexedHostsAndAlertsResponse } from '@kbn/security-solution-plugin/common/endpoint/index_data'; +import { + ENDPOINT_ARTIFACT_LIST_IDS, + EXCEPTION_LIST_URL, +} from '@kbn/securitysolution-list-constants'; +import { ArtifactElasticsearchProperties } from '@kbn/fleet-plugin/server/services'; +import { FtrProviderContext } from '../../ftr_provider_context'; +import { + ArtifactBodyType, + getArtifactsListTestsData, + ArtifactActionsType, + AgentPolicyResponseType, + getCreateMultipleData, + MultipleArtifactActionsType, +} from './mocks'; +import { PolicyTestResourceInfo } from '../../services/endpoint_policy'; +import { targetTags } from '../../target_tags'; + +export default ({ getPageObjects, getService }: FtrProviderContext) => { + const pageObjects = getPageObjects(['common', 'artifactEntriesList']); + const testSubjects = getService('testSubjects'); + const browser = getService('browser'); + const endpointArtifactsTestResources = getService('endpointArtifactTestResources'); + const endpointTestResources = getService('endpointTestResources'); + const retry = getService('retry'); + const esClient = getService('es'); + const supertest = getService('supertest'); + const find = getService('find'); + const toasts = getService('toasts'); + const policyTestResources = getService('policyTestResources'); + const unzipPromisify = promisify(unzip); + + const removeAllArtifacts = async () => { + for (const listId of ENDPOINT_ARTIFACT_LIST_IDS) { + await removeExceptionsList(listId); + } + }; + + const removeExceptionsList = async (listId: string) => { + await supertest + .delete(`${EXCEPTION_LIST_URL}?list_id=${listId}&namespace_type=agnostic`) + .set('kbn-xsrf', 'true'); + }; + + describe('For each artifact list under management', function () { + targetTags(this, ['@ess', '@serverless']); + + this.timeout(60_000 * 5); + let indexedData: IndexedHostsAndAlertsResponse; + let policyInfo: PolicyTestResourceInfo; + + before(async () => { + indexedData = await endpointTestResources.loadEndpointData(); + }); + after(async () => { + await endpointTestResources.unloadEndpointData(indexedData); + }); + + const checkFleetArtifacts = async ( + identifier: string, + expectedArtifact: ArtifactElasticsearchProperties, + expectedDecodedBodyArtifact: ArtifactBodyType, + policy?: PolicyTestResourceInfo + ) => { + // Check edited artifact is in the list with new values (wait for list to be updated) + let updatedArtifact: ArtifactElasticsearchProperties | undefined; + await retry.waitForWithTimeout('fleet artifact is updated', 120_000, async () => { + const artifacts = await endpointArtifactsTestResources.getArtifactsFromUnifiedManifestSO(); + + // This expects manifest artifact to come from unified so + const manifestArtifact = artifacts.find((artifact) => { + return ( + artifact.artifactIds.includes( + `${expectedArtifact.identifier}-${expectedArtifact.decoded_sha256}` + ) && artifact.policyId === policy?.packagePolicy.id + ); + }); + + if (!manifestArtifact) return false; + + // Get fleet artifact + const windowsArtifactResult = await esClient.get({ + index: '.fleet-artifacts-7', + id: `endpoint:${expectedArtifact.identifier}-${expectedArtifact.decoded_sha256}`, + }); + + const windowsArtifact = windowsArtifactResult._source as ArtifactElasticsearchProperties; + + // Get agent policy + const { + hits: { hits: policiesResults }, + } = await esClient.search({ + index: '.fleet-policies*', + query: { + bool: { + filter: [ + { + match: { + policy_id: policy?.agentPolicy.id, + }, + }, + ], + }, + }, + sort: [{ revision_idx: { order: 'desc' } }], + size: 1, + }); + + const agentPolicyResults = policiesResults[0] as AgentPolicyResponseType; + const policyArtifactManifest = agentPolicyResults._source.data.inputs[0] + ? agentPolicyResults._source.data.inputs[0].artifact_manifest + : undefined; + + let isUpdated: boolean = false; + if (policyArtifactManifest) { + // Compare artifacts from fleet artifacts and agent policy are the expecteds + isUpdated = + windowsArtifact.encoded_sha256 === expectedArtifact.encoded_sha256 && + policyArtifactManifest.artifacts[identifier].encoded_sha256 === + expectedArtifact.encoded_sha256; + } + + if (isUpdated) updatedArtifact = windowsArtifact; + return isUpdated; + }); + + updatedArtifact!.created = expectedArtifact.created; + const bodyFormBuffer = Buffer.from(updatedArtifact!.body, 'base64'); + const unzippedBody = await unzipPromisify(bodyFormBuffer); + + // Check decoded body first to detect possible body changes + expect(JSON.parse(unzippedBody.toString())).eql(expectedDecodedBodyArtifact); + expect(updatedArtifact).eql(expectedArtifact); + }; + + const performActions = async ( + actions: + | ArtifactActionsType['create']['formFields'] + | ArtifactActionsType['update']['formFields'], + suffix?: string + ) => { + for (const formAction of actions) { + if (formAction.type === 'customClick') { + await find.clickByCssSelector(formAction.selector, testSubjects.FIND_TIME); + } else if (formAction.type === 'click') { + await testSubjects.click(formAction.selector); + } else if (formAction.type === 'input') { + await testSubjects.setValue( + formAction.selector, + (formAction.value || '') + (suffix ? suffix : '') + ); + } else if (formAction.type === 'clear') { + await ( + await (await testSubjects.find(formAction.selector)).findByCssSelector('button') + ).click(); + } + } + }; + + const deleteArtifact = async (actions: ArtifactActionsType) => { + await pageObjects.artifactEntriesList.clickCardActionMenu(actions.pagePrefix); + await testSubjects.click(`${actions.pagePrefix}-card-cardDeleteAction`); + await testSubjects.click(`${actions.pagePrefix}-deleteModal-submitButton`); + await testSubjects.waitForDeleted(actions.delete.confirmSelector); + }; + + const createArtifact = async ( + actions: ArtifactActionsType | MultipleArtifactActionsType, + options?: { policyId?: string; suffix?: string; createButton?: string } + ) => { + // Opens add flyout + if (options?.createButton) { + await testSubjects.click(`${actions.pagePrefix}-${options.createButton}`); + } else { + await testSubjects.click(`${actions.pagePrefix}-emptyState-addButton`); + } + + await performActions(actions.create.formFields, options?.suffix); + + if (options?.policyId) { + await testSubjects.click(`${actions.pageObject}-form-effectedPolicies-perPolicy`); + await testSubjects.click(`policy-${options.policyId}-checkbox`); + } + + // Submit create artifact form + await testSubjects.click(`${actions.pagePrefix}-flyout-submitButton`); + }; + + const updateArtifact = async ( + actions: ArtifactActionsType, + options?: { policyId?: string; suffix?: string } + ) => { + // Opens edit flyout + await pageObjects.artifactEntriesList.clickCardActionMenu(actions.pagePrefix); + await testSubjects.click(`${actions.pagePrefix}-card-cardEditAction`); + + await performActions(actions.update.formFields); + + if (options?.policyId) { + await testSubjects.click(`${actions.pageObject}-form-effectedPolicies-perPolicy`); + await testSubjects.click(`policy-${options.policyId}-checkbox`); + } + + // Submit edit artifact form + await testSubjects.click(`${actions.pagePrefix}-flyout-submitButton`); + }; + + for (const testData of getArtifactsListTestsData()) { + describe(`When on the ${testData.title} entries list`, function () { + beforeEach(async () => { + policyInfo = await policyTestResources.createPolicy(); + await removeAllArtifacts(); + await browser.refresh(); + await pageObjects.artifactEntriesList.navigateToList(testData.urlPath); + }); + + afterEach(async () => { + await removeAllArtifacts(); + if (policyInfo) { + await policyInfo.cleanup(); + } + }); + + it(`should not show page title if there is no ${testData.title} entry`, async () => { + await testSubjects.missingOrFail('header-page-title'); + }); + + it(`should be able to add a new ${testData.title} entry`, async () => { + await createArtifact(testData, { policyId: policyInfo.packagePolicy.id }); + // Check new artifact is in the list + for (const checkResult of testData.create.checkResults) { + expect(await testSubjects.getVisibleText(checkResult.selector)).to.equal( + checkResult.value + ); + } + await toasts.dismiss(); + + // Title is shown after adding an item + expect(await testSubjects.getVisibleText('header-page-title')).to.equal(testData.title); + + // Checks if fleet artifact has been updated correctly + await checkFleetArtifacts( + testData.fleetArtifact.identifier, + testData.fleetArtifact.getExpectedUpdatedtArtifactWhenCreate(), + testData.fleetArtifact.getExpectedUpdatedArtifactBodyWhenCreate(), + policyInfo + ); + }); + + it(`should be able to update an existing ${testData.title} entry`, async () => { + await createArtifact(testData); + await updateArtifact(testData, { policyId: policyInfo.packagePolicy.id }); + + // Check edited artifact is in the list with new values (wait for list to be updated) + await retry.waitForWithTimeout('entry is updated in list', 20000, async () => { + const currentValue = await testSubjects.getVisibleText( + `${testData.pagePrefix}-card-criteriaConditions${ + testData.pagePrefix === 'EventFiltersListPage' ? '-condition' : '' + }` + ); + return currentValue === testData.update.waitForValue; + }); + + for (const checkResult of testData.update.checkResults) { + expect(await testSubjects.getVisibleText(checkResult.selector)).to.equal( + checkResult.value + ); + } + + await toasts.dismiss(); + + // Title still shown after editing an item + expect(await testSubjects.getVisibleText('header-page-title')).to.equal(testData.title); + + // Checks if fleet artifact has been updated correctly + await checkFleetArtifacts( + testData.fleetArtifact.identifier, + testData.fleetArtifact.getExpectedUpdatedArtifactWhenUpdate(), + testData.fleetArtifact.getExpectedUpdatedArtifactBodyWhenUpdate(), + policyInfo + ); + }); + + it(`should be able to delete the existing ${testData.title} entry`, async () => { + await createArtifact(testData); + await deleteArtifact(testData); + // We only expect one artifact to have been visible + await testSubjects.missingOrFail(testData.delete.card); + // Header has gone because there is no artifact + await testSubjects.missingOrFail('header-page-title'); + }); + }); + } + + describe('Should check artifacts are correctly generated when multiple entries', function () { + let firstPolicy: PolicyTestResourceInfo; + let secondPolicy: PolicyTestResourceInfo; + + const firstSuffix = 'first'; + const secondSuffix = 'second'; + const thirdSuffix = 'third'; + + beforeEach(async () => { + firstPolicy = await policyTestResources.createPolicy(); + secondPolicy = await policyTestResources.createPolicy(); + await removeAllArtifacts(); + await browser.refresh(); + await pageObjects.artifactEntriesList.navigateToList(testData.urlPath); + }); + + afterEach(async () => { + await removeAllArtifacts(); + if (firstPolicy) { + await firstPolicy.cleanup(); + } + if (secondPolicy) { + await secondPolicy.cleanup(); + } + }); + + const testData = getCreateMultipleData(); + it(`should get correct atifact when multiple entries are created`, async () => { + // Create first trusted app + await createArtifact(testData, { + policyId: firstPolicy.packagePolicy.id, + suffix: firstSuffix, + }); + await toasts.dismiss(); + + // Create second trusted app + await createArtifact(testData, { + policyId: secondPolicy.packagePolicy.id, + suffix: secondSuffix, + createButton: 'pageAddButton', + }); + await toasts.dismiss(); + + // Create third trusted app + await createArtifact(testData, { suffix: thirdSuffix, createButton: 'pageAddButton' }); + await toasts.dismiss(); + + // Checks if fleet artifact has been updated correctly + await checkFleetArtifacts( + testData.fleetArtifact.identifier, + testData.fleetArtifact.getExpectedUpdatedArtifactWhenCreateMultipleFirst(), + testData.fleetArtifact.getExpectedUpdatedArtifactBodyWhenCreateMultipleFirst( + thirdSuffix, + firstSuffix + ), + firstPolicy + ); + + // Checks if fleet artifact has been updated correctly + await checkFleetArtifacts( + testData.fleetArtifact.identifier, + testData.fleetArtifact.getExpectedUpdatedArtifactWhenCreateMultipleSecond(), + testData.fleetArtifact.getExpectedUpdatedArtifactBodyWhenCreateMultipleSecond( + thirdSuffix, + secondSuffix + ), + secondPolicy + ); + }); + }); + }); +}; diff --git a/x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/endpoint_exceptions.ts b/x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/endpoint_exceptions.ts new file mode 100644 index 0000000000000..ece54cecb47f1 --- /dev/null +++ b/x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/endpoint_exceptions.ts @@ -0,0 +1,245 @@ +/* + * Copyright 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 { unzip } from 'zlib'; +import { promisify } from 'util'; +import expect from '@kbn/expect'; +import { IndexedHostsAndAlertsResponse } from '@kbn/security-solution-plugin/common/endpoint/index_data'; +import { EXCEPTION_LIST_ITEM_URL } from '@kbn/securitysolution-list-constants'; +import { ArtifactElasticsearchProperties } from '@kbn/fleet-plugin/server/services'; +import { FoundExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { WebElementWrapper } from '@kbn/ftr-common-functional-ui-services'; +import { FtrProviderContext } from '../../ftr_provider_context'; +import { targetTags } from '../../target_tags'; + +export default ({ getPageObjects, getService }: FtrProviderContext) => { + const pageObjects = getPageObjects(['common', 'header']); + const queryBar = getService('queryBar'); + const testSubjects = getService('testSubjects'); + const endpointTestResources = getService('endpointTestResources'); + const endpointArtifactTestResources = getService('endpointArtifactTestResources'); + const retry = getService('retry'); + const esClient = getService('es'); + const supertest = getService('supertest'); + const find = getService('find'); + const unzipPromisify = promisify(unzip); + const comboBox = getService('comboBox'); + const toasts = getService('toasts'); + + describe('Endpoint Exceptions', function () { + targetTags(this, ['@ess', '@serverless']); + + this.timeout(10 * 60_000); + + const clearPrefilledEntries = async () => { + const entriesContainer = await testSubjects.find('exceptionEntriesContainer'); + + let deleteButtons: WebElementWrapper[]; + do { + deleteButtons = await testSubjects.findAllDescendant( + 'builderItemEntryDeleteButton', + entriesContainer + ); + + await deleteButtons[0].click(); + } while (deleteButtons.length > 1); + }; + + const openNewEndpointExceptionFlyout = async () => { + await testSubjects.click('timeline-context-menu-button'); + await testSubjects.click('add-endpoint-exception-menu-item'); + await testSubjects.existOrFail('addExceptionFlyout'); + + await retry.waitFor('entries should be loaded', () => + testSubjects.exists('exceptionItemEntryContainer') + ); + }; + + const setLastFieldsValue = async ({ + testSubj, + value, + }: { + testSubj: string; + value: string; + optionSelector?: string; + }) => { + const fields = await find.allByCssSelector(`[data-test-subj="${testSubj}"]`); + + const lastField = fields[fields.length - 1]; + await lastField.click(); + + await retry.try( + async () => { + await comboBox.setElement(lastField, value); + }, + async () => { + // If the above fails due to an option not existing, create the value custom instead + await comboBox.setFilterValue(lastField, value); + await pageObjects.common.pressEnterKey(); + } + ); + }; + + const setLastEntry = async ({ + field, + operator, + value, + }: { + field: string; + operator: 'matches' | 'is'; + value: string; + }) => { + await setLastFieldsValue({ testSubj: 'fieldAutocompleteComboBox', value: field }); + await setLastFieldsValue({ testSubj: 'operatorAutocompleteComboBox', value: operator }); + await setLastFieldsValue({ + testSubj: operator === 'matches' ? 'valuesAutocompleteWildcard' : 'valuesAutocompleteMatch', + value, + }); + }; + + const checkArtifact = (expectedArtifact: object) => { + return retry.tryForTime(120_000, async () => { + const artifacts = await endpointArtifactTestResources.getArtifactsFromUnifiedManifestSO(); + + const foundArtifactId = artifacts + .flatMap((artifact) => artifact.artifactIds) + .find((artifactId) => artifactId.startsWith('endpoint-exceptionlist-macos-v1')); + + expect(foundArtifactId).to.not.be(undefined); + + // Get fleet artifact + const artifactResult = await esClient.get({ + index: '.fleet-artifacts-7', + id: `endpoint:${foundArtifactId!}`, + }); + + const artifact = artifactResult._source as ArtifactElasticsearchProperties; + + const zippedBody = Buffer.from(artifact.body, 'base64'); + const artifactBody = await unzipPromisify(zippedBody); + + expect(JSON.parse(artifactBody.toString())).to.eql(expectedArtifact); + }); + }; + + let indexedData: IndexedHostsAndAlertsResponse; + before(async () => { + indexedData = await endpointTestResources.loadEndpointData(); + + const waitForAlertsToAppear = async () => { + await pageObjects.common.navigateToUrlWithBrowserHistory('security', `/alerts`); + await pageObjects.header.waitUntilLoadingHasFinished(); + await retry.waitForWithTimeout('alerts to appear', 10 * 60_000, async () => { + await queryBar.clickQuerySubmitButton(); + return testSubjects.exists('timeline-context-menu-button'); + }); + }; + + await waitForAlertsToAppear(); + }); + + after(async () => { + await endpointTestResources.unloadEndpointData(indexedData); + }); + + beforeEach(async () => { + const deleteEndpointExceptions = async () => { + const { body } = await supertest + .get(`${EXCEPTION_LIST_ITEM_URL}/_find?list_id=endpoint_list&namespace_type=agnostic`) + .set('kbn-xsrf', 'true'); + + for (const exceptionListItem of (body as FoundExceptionListItemSchema).data) { + await supertest + .delete(`${EXCEPTION_LIST_ITEM_URL}?id=${exceptionListItem.id}&namespace_type=agnostic`) + .set('kbn-xsrf', 'true'); + } + }; + + await deleteEndpointExceptions(); + }); + + it('should add `event.module=endpoint` to entry if only wildcard operator is present', async () => { + await pageObjects.common.navigateToUrlWithBrowserHistory('security', `/alerts`); + + await openNewEndpointExceptionFlyout(); + await clearPrefilledEntries(); + + await testSubjects.setValue('exceptionFlyoutNameInput', 'test exception'); + await setLastEntry({ field: 'file.path', operator: 'matches', value: '*/cheese/*' }); + await testSubjects.click('exceptionsAndButton'); + await setLastEntry({ field: 'process.executable', operator: 'matches', value: 'ex*' }); + + await testSubjects.click('addExceptionConfirmButton'); + await toasts.dismiss(); + + await checkArtifact({ + entries: [ + { + type: 'simple', + entries: [ + { + field: 'file.path', + operator: 'included', + type: 'wildcard_cased', + value: '*/cheese/*', + }, + { + field: 'process.executable', + operator: 'included', + type: 'wildcard_cased', + value: 'ex*', + }, + { + // this additional entry should be added + field: 'event.module', + operator: 'included', + type: 'exact_cased', + value: 'endpoint', + }, + ], + }, + ], + }); + }); + + it('should NOT add `event.module=endpoint` to entry if there is another operator', async () => { + await pageObjects.common.navigateToUrlWithBrowserHistory('security', `/alerts`); + + await openNewEndpointExceptionFlyout(); + await clearPrefilledEntries(); + + await testSubjects.setValue('exceptionFlyoutNameInput', 'test exception'); + await setLastEntry({ field: 'file.path', operator: 'matches', value: '*/cheese/*' }); + await testSubjects.click('exceptionsAndButton'); + await setLastEntry({ field: 'process.executable', operator: 'is', value: 'something' }); + + await testSubjects.click('addExceptionConfirmButton'); + await toasts.dismiss(); + + await checkArtifact({ + entries: [ + { + type: 'simple', + entries: [ + { + field: 'file.path', + operator: 'included', + type: 'wildcard_cased', + value: '*/cheese/*', + }, + { + field: 'process.executable', + operator: 'included', + type: 'exact_cased', + value: 'something', + }, + ], + }, + ], + }); + }); + }); +}; diff --git a/x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/index.ts b/x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/index.ts new file mode 100644 index 0000000000000..5464cf07f02e3 --- /dev/null +++ b/x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/index.ts @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getRegistryUrl as getRegistryUrlFromIngest } from '@kbn/fleet-plugin/server'; +import { isServerlessKibanaFlavor } from '@kbn/security-solution-plugin/scripts/endpoint/common/stack_services'; +import { FtrProviderContext } from '../../ftr_provider_context'; +import { + getRegistryUrlFromTestEnv, + isRegistryEnabled, +} from '../../../security_solution_endpoint_api_int/registry'; + +export default function (providerContext: FtrProviderContext) { + const { loadTestFile, getService, getPageObjects } = providerContext; + + describe('endpoint', function () { + const ingestManager = getService('ingestManager'); + const log = getService('log'); + const endpointTestResources = getService('endpointTestResources'); + const kbnClient = getService('kibanaServer'); + + if (!isRegistryEnabled()) { + log.warning('These tests are being run with an external package registry'); + } + + const registryUrl = getRegistryUrlFromTestEnv() ?? getRegistryUrlFromIngest(); + log.info(`Package registry URL for tests: ${registryUrl}`); + + before(async () => { + log.info('calling Fleet setup'); + await ingestManager.setup(); + + log.info('installing/upgrading Endpoint fleet package'); + await endpointTestResources.installOrUpgradeEndpointFleetPackage(); + + if (await isServerlessKibanaFlavor(kbnClient)) { + log.info('login for serverless environment'); + const pageObjects = getPageObjects(['svlCommonPage']); + await pageObjects.svlCommonPage.login(); + } + }); + loadTestFile(require.resolve('./artifact_entries_list')); + loadTestFile(require.resolve('./endpoint_exceptions')); + }); +} diff --git a/x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/mocks.ts b/x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/mocks.ts new file mode 100644 index 0000000000000..47523694b4349 --- /dev/null +++ b/x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/mocks.ts @@ -0,0 +1,807 @@ +/* + * Copyright 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 { FullAgentPolicy } from '@kbn/fleet-plugin/common/types'; +import { ArtifactElasticsearchProperties } from '@kbn/fleet-plugin/server/services/artifacts/types'; +import { InternalUnifiedManifestBaseSchema } from '@kbn/security-solution-plugin/server/endpoint/schemas/artifacts'; +import { TranslatedExceptionListItem } from '@kbn/security-solution-plugin/server/endpoint/schemas/artifacts/lists'; + +export interface AgentPolicyResponseType { + _index: string; + _id: string; + _score: number; + _source: { data: FullAgentPolicy }; +} + +export interface InternalUnifiedManifestSchemaResponseType { + _index: string; + _id: string; + _score: number; + _source: { + 'endpoint:unified-user-artifact-manifest': InternalUnifiedManifestBaseSchema; + }; +} + +export interface ArtifactBodyType { + entries: TranslatedExceptionListItem[]; +} + +export type ArtifactActionsType = ReturnType[0]; +export type MultipleArtifactActionsType = ReturnType; + +export const getArtifactsListTestsData = () => [ + { + title: 'Trusted applications', + pagePrefix: 'trustedAppsListPage', + create: { + formFields: [ + { + type: 'input', + selector: 'trustedApps-form-descriptionField', + value: 'This is the trusted application description', + }, + { + type: 'input', + selector: 'trustedApps-form-nameTextField', + value: 'Trusted application name', + }, + { + type: 'click', + selector: 'trustedApps-form-conditionsBuilder-group1-entry0-field', + }, + { + type: 'click', + selector: 'trustedApps-form-conditionsBuilder-group1-entry0-field-type-Hash', + }, + { + type: 'input', + selector: 'trustedApps-form-conditionsBuilder-group1-entry0-value', + value: 'A4370C0CF81686C0B696FA6261c9d3e0d810ae704ab8301839dffd5d5112f476', + }, + ], + checkResults: [ + { + selector: 'trustedAppsListPage-card-criteriaConditions', + value: + 'OSIS Windows\nAND process.hash.*IS a4370c0cf81686c0b696fa6261c9d3e0d810ae704ab8301839dffd5d5112f476', + }, + ], + }, + update: { + formFields: [ + { + type: 'input', + selector: 'trustedApps-form-descriptionField', + value: 'This is the trusted application description edited', + }, + { + type: 'input', + selector: 'trustedApps-form-nameTextField', + value: 'Trusted application name edited', + }, + { + type: 'click', + selector: 'trustedApps-form-conditionsBuilder-group1-entry0-field', + }, + { + type: 'click', + selector: 'trustedApps-form-conditionsBuilder-group1-entry0-field-type-Path', + }, + { + type: 'input', + selector: 'trustedApps-form-conditionsBuilder-group1-entry0-value', + value: 'c:\\randomFolder\\randomFile.exe, c:\\randomFolder\\randomFile2.exe', + }, + ], + checkResults: [ + { + selector: 'trustedAppsListPage-card-criteriaConditions', + value: + 'OSIS Windows\nAND process.executable.caselessIS c:\\randomFolder\\randomFile.exe, c:\\randomFolder\\randomFile2.exe', + }, + { + selector: 'trustedAppsListPage-card-header-title', + value: 'Trusted application name edited', + }, + { + selector: 'trustedAppsListPage-card-description', + value: 'This is the trusted application description edited', + }, + ], + waitForValue: + 'OSIS Windows\nAND process.executable.caselessIS c:\\randomFolder\\randomFile.exe, c:\\randomFolder\\randomFile2.exe', + }, + delete: { + confirmSelector: 'trustedAppsListPage-deleteModal-submitButton', + card: 'trustedAppsListPage-card', + }, + urlPath: 'trusted_apps', + pageObject: 'trustedApps', + fleetArtifact: { + identifier: 'endpoint-trustlist-windows-v1', + type: 'trustedApplications', + getExpectedUpdatedtArtifactWhenCreate: (): ArtifactElasticsearchProperties => ({ + type: 'trustlist', + identifier: 'endpoint-trustlist-windows-v1', + body: 'eJxVzNEKgyAUgOF3OdcxNMvMVxkxTp4jCa5EbWxE7z422MVuvx/+A3itOXABez2gvhKDhRLuKTI0f80HjgQWUt4cl3JZsCyXsmDba2hgS5yxbhkshNXFnZig+f34ia7eHJYvPjDuH8VODcIJ543URjsx61F71K2WbiTFgowUyIPocDZKSKNG8p566qVsfTdoOKdzOt89hz0Q', + package_name: 'endpoint', + created: '2000-01-01T00:00:00.000Z', + relative_url: + '/api/fleet/artifacts/endpoint-trustlist-windows-v1/016bec11c5b1d6f8609fd3525202aa12baf0132484abf368d5011100d5ec1ec4', + compression_algorithm: 'zlib', + decoded_size: 193, + decoded_sha256: '016bec11c5b1d6f8609fd3525202aa12baf0132484abf368d5011100d5ec1ec4', + encryption_algorithm: 'none', + encoded_sha256: '814aabc04d674ccdeb7c1acfe74120cb52ad1392d6924a7d813e08f8b6cd0f0f', + encoded_size: 153, + }), + getExpectedUpdatedArtifactBodyWhenCreate: (): ArtifactBodyType => ({ + entries: [ + { + type: 'simple', + entries: [ + { + field: 'process.hash.sha256', + operator: 'included', + type: 'exact_cased', + value: 'a4370c0cf81686c0b696fa6261c9d3e0d810ae704ab8301839dffd5d5112f476', + }, + ], + }, + ], + }), + getExpectedUpdatedArtifactWhenUpdate: (): ArtifactElasticsearchProperties => ({ + type: 'trustlist', + identifier: 'endpoint-trustlist-windows-v1', + body: 'eJx9jEEKwjAUBa8ibx1cuMwBvIQtEpMnBH6TkJ9KpeTuEkHBjcthhtnB1Gqkwl52tGchLDQuRQjz4+6REmBRavZUPXKjX5u7vcNcWF3LFRYxeVkDA8xnx835dvVOKVSFwcPJOoS301RdCnk5ZwmsX4rC8TeHf8VpJOhzn/sLJpZG8A==', + package_name: 'endpoint', + created: '2000-01-01T00:00:00.000Z', + relative_url: + '/api/fleet/artifacts/endpoint-trustlist-windows-v1/ac2bf74a73885f9a5a1700c328bf1a5a8f6cb72f2465a575335ea99dac0d4c10', + compression_algorithm: 'zlib', + decoded_size: 198, + decoded_sha256: 'ac2bf74a73885f9a5a1700c328bf1a5a8f6cb72f2465a575335ea99dac0d4c10', + encryption_algorithm: 'none', + encoded_sha256: '28d81b2787cea23fcb88d02b1c09940858963a62c60cdfd7a2b7564cfc251708', + encoded_size: 130, + }), + getExpectedUpdatedArtifactBodyWhenUpdate: (): ArtifactBodyType => ({ + entries: [ + { + type: 'simple', + entries: [ + { + field: 'process.executable', + operator: 'included', + type: 'exact_caseless', + value: 'c:\\randomFolder\\randomFile.exe, c:\\randomFolder\\randomFile2.exe', + }, + ], + }, + ], + }), + }, + }, + { + title: 'Event Filters', + pagePrefix: 'EventFiltersListPage', + create: { + formFields: [ + { + type: 'input', + selector: 'eventFilters-form-name-input', + value: 'Event filter name', + }, + { + type: 'input', + selector: 'eventFilters-form-description-input', + value: 'This is the event filter description', + }, + { + type: 'click', + selector: 'fieldAutocompleteComboBox', + }, + { + type: 'customClick', + selector: 'button[title="agent.ephemeral_id"]', + }, + { + type: 'click', + selector: 'valuesAutocompleteMatch', + }, + { + type: 'input', + selector: 'valuesAutocompleteMatch', + value: 'endpoint', + }, + ], + checkResults: [ + { + selector: 'EventFiltersListPage-card-criteriaConditions-condition', + value: 'AND agent.ephemeral_idIS endpoint', + }, + ], + }, + update: { + formFields: [ + { + type: 'input', + selector: 'eventFilters-form-name-input', + value: 'Event filter name edited', + }, + { + type: 'input', + selector: 'eventFilters-form-description-input', + value: 'This is the event filter description edited', + }, + { + type: 'click', + selector: 'fieldAutocompleteComboBox', + }, + { + type: 'input', + selector: 'fieldAutocompleteComboBox', + value: 'agent.id', + }, + { + type: 'customClick', + selector: 'button[title="agent.id"]', + }, + { + type: 'input', + selector: 'valuesAutocompleteMatch', + value: 'test super large value', + }, + { + type: 'click', + selector: 'eventFilters-form-description-input', + }, + ], + checkResults: [ + { + selector: 'EventFiltersListPage-card-criteriaConditions-condition', + value: 'AND agent.idIS test super large value', + }, + { + selector: 'EventFiltersListPage-card-header-title', + value: 'Event filter name edited', + }, + { + selector: 'EventFiltersListPage-card-description', + value: 'This is the event filter description edited', + }, + ], + waitForValue: 'AND agent.idIS test super large value', + }, + delete: { + confirmSelector: 'EventFiltersListPage-deleteModal-submitButton', + card: 'EventFiltersListPage-card', + }, + urlPath: 'event_filters', + pageObject: 'eventFilters', + fleetArtifact: { + identifier: 'endpoint-eventfilterlist-windows-v1', + type: 'eventfilterlist', + getExpectedUpdatedtArtifactWhenCreate: (): ArtifactElasticsearchProperties => ({ + type: 'eventfilterlist', + identifier: 'endpoint-eventfilterlist-windows-v1', + body: 'eJxVzFEKwjAQRdG9vO/iArKVUsqQPHVgmoRkWpSSvYvFH3/PhXuC2ZuyI8wn/F2JgK5bNWL6a3elJQTIg9lvrE9ubGKrJkwolU28NARojrYnfvW340uir1H6hYfYfmlOtWh2jGUs4wOrCC+X', + package_name: 'endpoint', + created: '2000-01-01T00:00:00.000Z', + relative_url: + '/api/fleet/artifacts/endpoint-eventfilterlist-windows-v1/b3373c93ffc795d954f22c625c084dc5874a156ec0cb3d4af1c3dab0b965fa30', + compression_algorithm: 'zlib', + decoded_size: 136, + decoded_sha256: 'b3373c93ffc795d954f22c625c084dc5874a156ec0cb3d4af1c3dab0b965fa30', + encryption_algorithm: 'none', + encoded_sha256: 'cc9bc4e3cc2c2767c3f56b17ebf4901dbe7e82f15720d48c745370e028c5e887', + encoded_size: 108, + }), + getExpectedUpdatedArtifactBodyWhenCreate: (): ArtifactBodyType => ({ + entries: [ + { + type: 'simple', + entries: [ + { + field: 'agent.ephemeral_id', + operator: 'included', + type: 'exact_cased', + value: 'endpoint', + }, + ], + }, + ], + }), + getExpectedUpdatedArtifactWhenUpdate: (): ArtifactElasticsearchProperties => ({ + type: 'eventfilterlist', + identifier: 'endpoint-eventfilterlist-windows-v1', + body: 'eJxVzEEKwyAURdGtyBuHLsCtlFA++hoEa+T7LQnBvZc0nXR6LtwDLKaJDf5+wPZKeLT0qpmY/tozMUd4yMJitxQxYa1UsVXhkUrIPfLU34SbBHsEaV98S+6nGpu51ivVZdGF7gpjHvP4ADqUMJs=', + package_name: 'endpoint', + created: '2000-01-01T00:00:00.000Z', + relative_url: + '/api/fleet/artifacts/endpoint-eventfilterlist-windows-v1/e4f00c88380d2c429eeb2741ad19383b94d76f79744b098b095befc24003e158', + compression_algorithm: 'zlib', + decoded_size: 140, + decoded_sha256: 'e4f00c88380d2c429eeb2741ad19383b94d76f79744b098b095befc24003e158', + encryption_algorithm: 'none', + encoded_sha256: 'e371e2a28b59bd942ca7ef9665dae7c9b27409ad6f2ca3bff6357a98deb23c12', + encoded_size: 110, + }), + getExpectedUpdatedArtifactBodyWhenUpdate: (): ArtifactBodyType => ({ + entries: [ + { + type: 'simple', + entries: [ + { + field: 'agent.id', + operator: 'included', + type: 'exact_cased', + value: 'test super large value', + }, + ], + }, + ], + }), + }, + }, + { + title: 'Blocklist', + pagePrefix: 'blocklistPage', + create: { + formFields: [ + { + type: 'input', + selector: 'blocklist-form-name-input', + value: 'Blocklist name', + }, + { + type: 'input', + selector: 'blocklist-form-description-input', + value: 'This is the blocklist description', + }, + { + type: 'click', + selector: 'blocklist-form-field-select', + }, + { + type: 'click', + selector: 'blocklist-form-file.hash.*', + }, + { + type: 'input', + selector: 'blocklist-form-values-input', + value: + 'A4370C0CF81686C0B696FA6261c9d3e0d810ae704ab8301839dffd5d5112f476,aedb279e378BED6C2DB3C9DC9e12ba635e0b391c,741462ab431a22233C787BAAB9B653C7', + }, + { + type: 'click', + selector: 'blocklist-form-name-input', + }, + ], + checkResults: [ + { + selector: 'blocklistPage-card-criteriaConditions', + value: + 'OSIS Windows\nAND file.hash.*IS ONE OF\n741462ab431a22233c787baab9b653c7\naedb279e378bed6c2db3c9dc9e12ba635e0b391c\na4370c0cf81686c0b696fa6261c9d3e0d810ae704ab8301839dffd5d5112f476', + }, + ], + }, + update: { + formFields: [ + { + type: 'input', + selector: 'blocklist-form-name-input', + value: 'Blocklist name edited', + }, + { + type: 'input', + selector: 'blocklist-form-description-input', + value: 'This is the blocklist description edited', + }, + { + type: 'click', + selector: 'blocklist-form-field-select', + }, + { + type: 'click', + selector: 'blocklist-form-file.path.caseless', + }, + { + type: 'clear', + selector: + 'blocklist-form-values-input-a4370c0cf81686c0b696fa6261c9d3e0d810ae704ab8301839dffd5d5112f476', + }, + { + type: 'clear', + selector: 'blocklist-form-values-input-741462ab431a22233c787baab9b653c7', + }, + { + type: 'clear', + selector: 'blocklist-form-values-input-aedb279e378bed6c2db3c9dc9e12ba635e0b391c', + }, + { + type: 'input', + selector: 'blocklist-form-values-input', + value: 'c:\\randomFolder\\randomFile.exe, c:\\randomFolder\\randomFile2.exe', + }, + { + type: 'click', + selector: 'blocklist-form-name-input', + }, + ], + checkResults: [ + { + selector: 'blocklistPage-card-criteriaConditions', + value: + 'OSIS Windows\nAND file.path.caselessIS ONE OF\nc:\\randomFolder\\randomFile.exe\nc:\\randomFolder\\randomFile2.exe', + }, + { + selector: 'blocklistPage-card-header-title', + value: 'Blocklist name edited', + }, + { + selector: 'blocklistPage-card-description', + value: 'This is the blocklist description edited', + }, + ], + waitForValue: + 'OSIS Windows\nAND file.path.caselessIS ONE OF\nc:\\randomFolder\\randomFile.exe\nc:\\randomFolder\\randomFile2.exe', + }, + delete: { + confirmSelector: 'blocklistDeletionConfirm', + card: 'blocklistCard', + }, + pageObject: 'blocklist', + urlPath: 'blocklist', + fleetArtifact: { + identifier: 'endpoint-blocklist-windows-v1', + type: 'blocklist', + getExpectedUpdatedtArtifactWhenCreate: (): ArtifactElasticsearchProperties => ({ + type: 'blocklist', + identifier: 'endpoint-blocklist-windows-v1', + relative_url: + '/api/fleet/artifacts/endpoint-blocklist-windows-v1/637f1e8795406904980ae2ab4a69cea967756571507f6bd7fc94cde0add20df2', + body: 'eJylzk1qw0AMQOG7aG3C/GpmfJVggkbSYIPjmNgpDcF3LxS66LLN+sHje4Eu+33SDfrzC/bnqtDDNl3XWaH71dqks0APbZr1NNI2nq4SoYPbqnfab3foYVp4fogKdD8n/STeL0ybyoWWJ3TwQfNDoT9DCjagoxq8Jeec95xyqkS1VIyeEwzHcHR/NW0j2TdQpFJdKupTrirITqrnIlzUukroo5rqi+V/41zEd3jBJ8OGW7aYkU3Fgo3QoeUiXo1ka0iTCVSzNzb7Iq1JlGitayHhN3s4vgDTjqDt', + encryption_algorithm: 'none', + package_name: 'endpoint', + encoded_size: 219, + encoded_sha256: 'e803c1ee6aec0885092bfd6c288839f42b31107dd6d0bb2c8e2d2b9f8fc8b293', + decoded_size: 501, + decoded_sha256: '637f1e8795406904980ae2ab4a69cea967756571507f6bd7fc94cde0add20df2', + compression_algorithm: 'zlib', + created: '2000-01-01T00:00:00.000Z', + }), + getExpectedUpdatedArtifactBodyWhenCreate: (): ArtifactBodyType => ({ + entries: [ + { + type: 'simple', + entries: [ + { + field: 'file.hash.md5', + operator: 'included', + type: 'exact_cased_any', + value: ['741462ab431a22233c787baab9b653c7'], + }, + ], + }, + { + type: 'simple', + entries: [ + { + field: 'file.hash.sha1', + operator: 'included', + type: 'exact_cased_any', + value: ['aedb279e378bed6c2db3c9dc9e12ba635e0b391c'], + }, + ], + }, + { + type: 'simple', + entries: [ + { + field: 'file.hash.sha256', + operator: 'included', + type: 'exact_cased_any', + value: ['a4370c0cf81686c0b696fa6261c9d3e0d810ae704ab8301839dffd5d5112f476'], + }, + ], + }, + ], + }), + getExpectedUpdatedArtifactWhenUpdate: (): ArtifactElasticsearchProperties => ({ + type: 'blocklist', + identifier: 'endpoint-blocklist-windows-v1', + relative_url: + '/api/fleet/artifacts/endpoint-blocklist-windows-v1/3ead6ce4e34cb4411083a44bfe813d9442d296981ee8d56e727e6cff14dea0f0', + body: 'eJx9jUEKwjAURK8isw4uXOYAXqKV8kmmGPhNQpJKS/HuEkHBjcxqmMebA4ytBFbY4UDbM2FRw5KVMD/bHKgeFnNQnrO0OwxSZpGWCixCdLp6epiPhZu4NjmpVNY6Sdxh8BBdCTvA2XEsEn1arkk9y7d1Pbf+fvrHXN7Q7dnzAojqRb8=', + encryption_algorithm: 'none', + package_name: 'endpoint', + encoded_size: 131, + encoded_sha256: 'f0e2dc2aa8d968b704baa11bf3100db91a85991d5de431f8c389b7417335a701', + decoded_size: 197, + decoded_sha256: '3ead6ce4e34cb4411083a44bfe813d9442d296981ee8d56e727e6cff14dea0f0', + compression_algorithm: 'zlib', + created: '2000-01-01T00:00:00.000Z', + }), + getExpectedUpdatedArtifactBodyWhenUpdate: (): ArtifactBodyType => ({ + entries: [ + { + type: 'simple', + entries: [ + { + field: 'file.path', + operator: 'included', + type: 'exact_caseless_any', + value: ['c:\\randomFolder\\randomFile.exe', ' c:\\randomFolder\\randomFile2.exe'], + }, + ], + }, + ], + }), + }, + }, + { + title: 'Host isolation exceptions', + pagePrefix: 'hostIsolationExceptionsListPage', + create: { + formFields: [ + { + type: 'input', + selector: 'hostIsolationExceptions-form-name-input', + value: 'Host Isolation exception name', + }, + { + type: 'input', + selector: 'hostIsolationExceptions-form-description-input', + value: 'This is the host isolation exception description', + }, + { + type: 'input', + selector: 'hostIsolationExceptions-form-ip-input', + value: '1.1.1.1', + }, + ], + checkResults: [ + { + selector: 'hostIsolationExceptionsListPage-card-criteriaConditions', + value: 'OSIS Windows, Linux, Mac\nAND destination.ipIS 1.1.1.1', + }, + ], + }, + update: { + formFields: [ + { + type: 'input', + selector: 'hostIsolationExceptions-form-name-input', + value: 'Host Isolation exception name edited', + }, + { + type: 'input', + selector: 'hostIsolationExceptions-form-description-input', + value: 'This is the host isolation exception description edited', + }, + { + type: 'input', + selector: 'hostIsolationExceptions-form-ip-input', + value: '2.2.2.2/24', + }, + ], + checkResults: [ + { + selector: 'hostIsolationExceptionsListPage-card-criteriaConditions', + value: 'OSIS Windows, Linux, Mac\nAND destination.ipIS 2.2.2.2/24', + }, + { + selector: 'hostIsolationExceptionsListPage-card-header-title', + value: 'Host Isolation exception name edited', + }, + { + selector: 'hostIsolationExceptionsListPage-card-description', + value: 'This is the host isolation exception description edited', + }, + ], + waitForValue: 'OSIS Windows, Linux, Mac\nAND destination.ipIS 2.2.2.2/24', + }, + delete: { + confirmSelector: 'hostIsolationExceptionsDeletionConfirm', + card: 'hostIsolationExceptionsCard', + }, + pageObject: 'hostIsolationExceptions', + urlPath: 'host_isolation_exceptions', + fleetArtifact: { + identifier: 'endpoint-hostisolationexceptionlist-windows-v1', + type: 'hostisolationexceptionlist', + getExpectedUpdatedtArtifactWhenCreate: (): ArtifactElasticsearchProperties => ({ + type: 'hostisolationexceptionlist', + identifier: 'endpoint-hostisolationexceptionlist-windows-v1', + relative_url: + '/api/fleet/artifacts/endpoint-hostisolationexceptionlist-windows-v1/2c3ee2b5e7f86f8c336a3df7e59a1151b11d7eec382442032e69712d6a6459e0', + body: 'eJxVjEEKgzAUBe/y1kFwm6uIyCd5hQ9pEpKvWCR3LxVclNnNwFxgtqbs8MsF+1TCo+u7JsL9tZcyRXhEdtMspiVPWuFQKptYafDQHNIeGeGeFU8JtgXptzwk7T87TzcY61jHF647LBE=', + encryption_algorithm: 'none', + package_name: 'endpoint', + encoded_size: 104, + encoded_sha256: 'f958ada742a0be63d136901317c6bfd04b2ab5f52cdd0e872461089b0009bb3e', + decoded_size: 131, + decoded_sha256: '2c3ee2b5e7f86f8c336a3df7e59a1151b11d7eec382442032e69712d6a6459e0', + compression_algorithm: 'zlib', + created: '2000-01-01T00:00:00.000Z', + }), + getExpectedUpdatedArtifactBodyWhenCreate: (): ArtifactBodyType => ({ + entries: [ + { + type: 'simple', + entries: [ + { + field: 'destination.ip', + operator: 'included', + type: 'exact_cased', + value: '1.1.1.1', + }, + ], + }, + ], + }), + getExpectedUpdatedArtifactWhenUpdate: (): ArtifactElasticsearchProperties => ({ + type: 'hostisolationexceptionlist', + identifier: 'endpoint-hostisolationexceptionlist-windows-v1', + relative_url: + '/api/fleet/artifacts/endpoint-hostisolationexceptionlist-windows-v1/4b62473b4cf057277b3297896771cc1061c3bea2c4f7ec1ef5c2546f33d5d9e8', + body: 'eJxVjEEKwyAUBe/y1pJC6MqrlBA++gofrIr+hJbg3UsCXZTZzcAcYLam7PCPA/aphEfXV02E+2tPZYrwiOymWUxLnrTCoVQ2sdLgoTmkLTLC/VZ8S7A1SL/kLmk77Txd3OY7xjKW8QUwWyyq', + encryption_algorithm: 'none', + package_name: 'endpoint', + encoded_size: 108, + encoded_sha256: '84df618343078f43a54299bcebef03010f3ec4ffdf7160448882fee9bafa1adb', + decoded_size: 134, + decoded_sha256: '4b62473b4cf057277b3297896771cc1061c3bea2c4f7ec1ef5c2546f33d5d9e8', + compression_algorithm: 'zlib', + created: '2000-01-01T00:00:00.000Z', + }), + getExpectedUpdatedArtifactBodyWhenUpdate: (): ArtifactBodyType => ({ + entries: [ + { + type: 'simple', + entries: [ + { + field: 'destination.ip', + operator: 'included', + type: 'exact_cased', + value: '2.2.2.2/24', + }, + ], + }, + ], + }), + }, + }, +]; + +export const getCreateMultipleData = () => ({ + title: 'Trusted applications', + pagePrefix: 'trustedAppsListPage', + create: { + formFields: [ + { + type: 'input', + selector: 'trustedApps-form-descriptionField', + value: 'This is the trusted application description', + }, + { + type: 'input', + selector: 'trustedApps-form-nameTextField', + value: 'Trusted application name', + }, + { + type: 'click', + selector: 'trustedApps-form-conditionsBuilder-group1-entry0-field', + }, + { + type: 'click', + selector: 'trustedApps-form-conditionsBuilder-group1-entry0-field-type-Path', + }, + { + type: 'input', + selector: 'trustedApps-form-conditionsBuilder-group1-entry0-value', + value: 'c:\\randomFolder\\randomFile.exe', + }, + ], + }, + + urlPath: 'trusted_apps', + pageObject: 'trustedApps', + fleetArtifact: { + identifier: 'endpoint-trustlist-windows-v1', + type: 'trustedApplications', + getExpectedUpdatedArtifactWhenCreateMultipleFirst: (): ArtifactElasticsearchProperties => ({ + type: 'trustlist', + identifier: 'endpoint-trustlist-windows-v1', + body: 'eJzNjlEKwjAQBe+y38ED5ABewhaJySsubJuwu5VK6d0lgoI38PMxj2F2wuLKMIqXnfzZQJGM5yag8MMmhhSK1LRmmJ2wIa+ebu9jbdDkVSkSL1nWgkLho8OWsl9zMgjMKNAjydpBjsOgaSl1Plcp0O9iQff7nbXQMR7h79ImVvOeNh4vUR5zdA==', + package_name: 'endpoint', + created: '2000-01-01T00:00:00.000Z', + relative_url: + '/api/fleet/artifacts/endpoint-trustlist-windows-v1/329fc9176a24d64f4376d2c25d5db5b31cf86b288dac83c8a004dfe5bbfdc7d0', + compression_algorithm: 'zlib', + decoded_size: 323, + decoded_sha256: '329fc9176a24d64f4376d2c25d5db5b31cf86b288dac83c8a004dfe5bbfdc7d0', + encryption_algorithm: 'none', + encoded_sha256: '4d9eecb830948eabd721563fd2473900207d043126e66eac2ef78f9e05a80adb', + encoded_size: 136, + }), + getExpectedUpdatedArtifactBodyWhenCreateMultipleFirst: ( + firstSuffix: string, + secondSuffix: string + ): ArtifactBodyType => ({ + entries: [ + { + type: 'simple', + entries: [ + { + field: 'process.executable', + operator: 'included', + type: 'exact_caseless', + value: `c:\\randomFolder\\randomFile.exe${firstSuffix}`, + }, + ], + }, + { + entries: [ + { + field: 'process.executable', + operator: 'included', + type: 'exact_caseless', + value: `c:\\randomFolder\\randomFile.exe${secondSuffix}`, + }, + ], + type: 'simple', + }, + ], + }), + getExpectedUpdatedArtifactWhenCreateMultipleSecond: (): ArtifactElasticsearchProperties => ({ + type: 'trustlist', + identifier: 'endpoint-trustlist-windows-v1', + body: 'eJzNjlEKwjAQRO8y38ED5ABewhaJyYiBbRJ2U6mU3l1aUPAGfg5veLwVLF0zDf6yor8a4WF5akK4H3bPlASPpjXS7MSFce7hdhxro4ZeFR65RJkTE9xHxyXEfo3BKDSDwzPIvIPoh0FDSXU6V0nU78rC3d8fWRO2cXN/l2aMtRxt4/YGxIFzyA==', + package_name: 'endpoint', + created: '2000-01-01T00:00:00.000Z', + relative_url: + '/api/fleet/artifacts/endpoint-trustlist-windows-v1/3be2ce848f9b49d6531e6dc80f43579e00adbc640d3f785c14c8f9fa2652500a', + compression_algorithm: 'zlib', + decoded_size: 324, + decoded_sha256: '3be2ce848f9b49d6531e6dc80f43579e00adbc640d3f785c14c8f9fa2652500a', + encryption_algorithm: 'none', + encoded_sha256: '68304c35bbe863d0fbb15cf7e5ae5c84bad17aa7a3bc26828f9f0b20e0df6ed8', + encoded_size: 136, + }), + getExpectedUpdatedArtifactBodyWhenCreateMultipleSecond: ( + firstSuffix: string, + secondSuffix: string + ): ArtifactBodyType => ({ + entries: [ + { + type: 'simple', + entries: [ + { + field: 'process.executable', + operator: 'included', + type: 'exact_caseless', + value: `c:\\randomFolder\\randomFile.exe${firstSuffix}`, + }, + ], + }, + { + entries: [ + { + field: 'process.executable', + operator: 'included', + type: 'exact_caseless', + value: `c:\\randomFolder\\randomFile.exe${secondSuffix}`, + }, + ], + type: 'simple', + }, + ], + }), + }, +}); diff --git a/x-pack/test/security_solution_endpoint/integrations_feature_flag.config.ts b/x-pack/test/security_solution_endpoint/integrations_feature_flag.config.ts new file mode 100644 index 0000000000000..275f984307bff --- /dev/null +++ b/x-pack/test/security_solution_endpoint/integrations_feature_flag.config.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { resolve } from 'path'; +import { FtrConfigProviderContext } from '@kbn/test'; +import { generateConfig } from './config.base'; +import { services } from './services'; + +export default async function (ftrConfigProviderContext: FtrConfigProviderContext) { + const { readConfigFile } = ftrConfigProviderContext; + + const xpackFunctionalConfig = await readConfigFile( + require.resolve('../functional/config.base.js') + ); + + return generateConfig({ + ftrConfigProviderContext, + baseConfig: xpackFunctionalConfig, + testFiles: [resolve(__dirname, './apps/integrations_feature_flag')], + junitReportName: + 'X-Pack Endpoint Integrations With Feature Flags turned on Functional Tests on ESS', + target: 'ess', + kbnServerArgs: [ + // set the packagerTaskInterval to 5s in order to speed up test executions when checking fleet artifacts + '--xpack.securitySolution.packagerTaskInterval=5s', + `--xpack.securitySolution.enableExperimental=${JSON.stringify(['unifiedManifestEnabled'])}`, + ], + services, + }); +} diff --git a/x-pack/test/security_solution_endpoint/serverless.integrations_feature_flag.config.ts b/x-pack/test/security_solution_endpoint/serverless.integrations_feature_flag.config.ts new file mode 100644 index 0000000000000..7ac35bbe3a101 --- /dev/null +++ b/x-pack/test/security_solution_endpoint/serverless.integrations_feature_flag.config.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { resolve } from 'path'; +import { FtrConfigProviderContext } from '@kbn/test'; +import { generateConfig } from './config.base'; +import { svlServices } from './services'; + +export default async function (ftrConfigProviderContext: FtrConfigProviderContext) { + const { readConfigFile } = ftrConfigProviderContext; + + const svlBaseConfig = await readConfigFile( + require.resolve('../../test_serverless/shared/config.base.ts') + ); + + return generateConfig({ + ftrConfigProviderContext, + baseConfig: svlBaseConfig, + testFiles: [resolve(__dirname, './apps/integrations_feature_flag')], + junitReportName: + 'X-Pack Endpoint Integrations With Feature Flags turned on Functional Tests on ESS', + target: 'serverless', + kbnServerArgs: [ + '--serverless=security', + // set the packagerTaskInterval to 5s in order to speed up test executions when checking fleet artifacts + '--xpack.securitySolution.packagerTaskInterval=5s', + `--xpack.securitySolution.enableExperimental=${JSON.stringify(['unifiedManifestEnabled'])}`, + ], + services: svlServices, + }); +} diff --git a/x-pack/test/security_solution_endpoint/services/endpoint_artifacts.ts b/x-pack/test/security_solution_endpoint/services/endpoint_artifacts.ts index a8fcbf3f982fa..0a914226c9280 100644 --- a/x-pack/test/security_solution_endpoint/services/endpoint_artifacts.ts +++ b/x-pack/test/security_solution_endpoint/services/endpoint_artifacts.ts @@ -6,9 +6,9 @@ */ import type { - ExceptionListItemSchema, - CreateExceptionListSchema, CreateExceptionListItemSchema, + CreateExceptionListSchema, + ExceptionListItemSchema, } from '@kbn/securitysolution-io-ts-list-types'; import { EXCEPTION_LIST_ITEM_URL, EXCEPTION_LIST_URL } from '@kbn/securitysolution-list-constants'; import { Response } from 'superagent'; @@ -19,8 +19,10 @@ import { EVENT_FILTER_LIST_DEFINITION } from '@kbn/security-solution-plugin/publ import { HOST_ISOLATION_EXCEPTIONS_LIST_DEFINITION } from '@kbn/security-solution-plugin/public/management/pages/host_isolation_exceptions/constants'; import { BLOCKLISTS_LIST_DEFINITION } from '@kbn/security-solution-plugin/public/management/pages/blocklist/constants'; import { ManifestConstants } from '@kbn/security-solution-plugin/server/endpoint/lib/artifacts'; + import { FtrService } from '../../functional/ftr_provider_context'; import { InternalManifestSchemaResponseType } from '../apps/integrations/mocks'; +import { InternalUnifiedManifestSchemaResponseType } from '../apps/integrations_feature_flag/mocks'; export interface ArtifactTestData { artifact: ExceptionListItemSchema; @@ -132,8 +134,25 @@ export class EndpointArtifactsTestResources extends FtrService { }); const manifestResult = manifestResults[0] as InternalManifestSchemaResponseType; - const artifacts = manifestResult._source['endpoint:user-artifact-manifest'].artifacts; + return manifestResult._source['endpoint:user-artifact-manifest'].artifacts; + } + + async getArtifactsFromUnifiedManifestSO(): Promise< + Array< + InternalUnifiedManifestSchemaResponseType['_source']['endpoint:unified-user-artifact-manifest'] + > + > { + const { + hits: { hits: manifestResults }, + } = await this.esClient.search({ + index: '.kibana*', + query: { + bool: { filter: [{ term: { type: ManifestConstants.UNIFIED_SAVED_OBJECT_TYPE } }] }, + }, + }); - return artifacts; + return manifestResults.map( + (result) => result._source!['endpoint:unified-user-artifact-manifest'] + ); } } diff --git a/x-pack/test/spaces_api_integration/common/lib/create_users_and_roles.ts b/x-pack/test/spaces_api_integration/common/lib/create_users_and_roles.ts index 15ee9785aa690..58ef5ba9f9481 100644 --- a/x-pack/test/spaces_api_integration/common/lib/create_users_and_roles.ts +++ b/x-pack/test/spaces_api_integration/common/lib/create_users_and_roles.ts @@ -5,11 +5,11 @@ * 2.0. */ -import { SuperTest } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import type { Client } from '@elastic/elasticsearch'; import { AUTHENTICATION } from './authentication'; -export const createUsersAndRoles = async (es: Client, supertest: SuperTest) => { +export const createUsersAndRoles = async (es: Client, supertest: SuperTestAgent) => { await supertest .put('/api/security/role/kibana_legacy_user') .send({ diff --git a/x-pack/test/spaces_api_integration/common/suites/disable_legacy_url_aliases.ts b/x-pack/test/spaces_api_integration/common/suites/disable_legacy_url_aliases.ts index 002ed6c3e6515..5889a10479f31 100644 --- a/x-pack/test/spaces_api_integration/common/suites/disable_legacy_url_aliases.ts +++ b/x-pack/test/spaces_api_integration/common/suites/disable_legacy_url_aliases.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import type { SuperTest } from 'supertest'; +import type { Agent as SuperTestAgent } from 'supertest'; import type { Client } from '@elastic/elasticsearch'; import type { LegacyUrlAlias } from '@kbn/core-saved-objects-base-server-internal'; import { MAIN_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; @@ -48,7 +48,7 @@ const getTestTitle = ({ targetSpace, targetType, sourceId }: DisableLegacyUrlAli export function disableLegacyUrlAliasesTestSuiteFactory( es: Client, esArchiver: any, - supertest: SuperTest + supertest: SuperTestAgent ) { const expectResponseBody = (testCase: DisableLegacyUrlAliasesTestCase, statusCode: 204 | 403): ExpectResponseBody => @@ -117,7 +117,7 @@ export function disableLegacyUrlAliasesTestSuiteFactory( const requestBody = test.request; await supertest .post(`${getUrlPrefix(spaceId)}/api/spaces/_disable_legacy_url_aliases`) - .auth(user?.username, user?.password) + .auth(user?.username!, user?.password!) .send(requestBody) .expect(test.responseStatusCode) .then(test.responseBody); diff --git a/x-pack/test/spaces_api_integration/common/suites/get_shareable_references.ts b/x-pack/test/spaces_api_integration/common/suites/get_shareable_references.ts index bedc8a52409b6..c1630bc288169 100644 --- a/x-pack/test/spaces_api_integration/common/suites/get_shareable_references.ts +++ b/x-pack/test/spaces_api_integration/common/suites/get_shareable_references.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import { deepFreeze } from '@kbn/std'; -import { SuperTest } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { SavedObjectsCollectMultiNamespaceReferencesResponse, SavedObjectReferenceWithContext, @@ -192,7 +192,7 @@ const getRedactedSpaces = (authorizedSpace: string | undefined, spaces: string[] return redactedSpaces.sort((a, b) => (a === '?' ? 1 : b === '?' ? -1 : 0)); // unknown spaces are always at the end of the array }; -export function getShareableReferencesTestSuiteFactory(esArchiver: any, supertest: SuperTest) { +export function getShareableReferencesTestSuiteFactory(esArchiver: any, supertest: SuperTestAgent) { const expectForbidden = expectResponses.forbiddenTypes('share_to_space'); const expectResponseBody = ( @@ -274,7 +274,7 @@ export function getShareableReferencesTestSuiteFactory(esArchiver: any, supertes const requestBody = test.request; await supertest .post(`${getUrlPrefix(spaceId)}/api/spaces/_get_shareable_references`) - .auth(user?.username, user?.password) + .auth(user?.username!, user?.password!) .send(requestBody) .expect(test.responseStatusCode) .then(test.responseBody); diff --git a/x-pack/test/spaces_api_integration/common/suites/update_objects_spaces.ts b/x-pack/test/spaces_api_integration/common/suites/update_objects_spaces.ts index 14eb97c38c6ee..777581d9aa5a0 100644 --- a/x-pack/test/spaces_api_integration/common/suites/update_objects_spaces.ts +++ b/x-pack/test/spaces_api_integration/common/suites/update_objects_spaces.ts @@ -9,7 +9,7 @@ import expect from '@kbn/expect'; import type { Client } from '@elastic/elasticsearch'; import type { SearchTotalHits } from '@elastic/elasticsearch/lib/api/types'; import { without, uniq } from 'lodash'; -import { SuperTest } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { SavedObjectsErrorHelpers, SavedObjectsUpdateObjectsSpacesResponse, @@ -61,7 +61,7 @@ const getTestTitle = ({ objects, spacesToAdd, spacesToRemove }: UpdateObjectsSpa export function updateObjectsSpacesTestSuiteFactory( es: Client, esArchiver: any, - supertest: SuperTest + supertest: SuperTestAgent ) { const expectForbidden = expectResponses.forbiddenTypes('share_to_space'); const expectResponseBody = @@ -162,7 +162,7 @@ export function updateObjectsSpacesTestSuiteFactory( const requestBody = test.request; await supertest .post(`${getUrlPrefix(spaceId)}/api/spaces/_update_objects_spaces`) - .auth(user?.username, user?.password) + .auth(user?.username!, user?.password!) .send(requestBody) .expect(test.responseStatusCode) .then(test.responseBody); diff --git a/x-pack/test_serverless/api_integration/services/slo_api.ts b/x-pack/test_serverless/api_integration/services/slo_api.ts index 5a5c9eb5c5ff7..7312905589cb7 100644 --- a/x-pack/test_serverless/api_integration/services/slo_api.ts +++ b/x-pack/test_serverless/api_integration/services/slo_api.ts @@ -5,6 +5,11 @@ * 2.0. */ +import { + fetchHistoricalSummaryParamsSchema, + FetchHistoricalSummaryResponse, +} from '@kbn/slo-schema'; +import * as t from 'io-ts'; import { FtrProviderContext } from '../ftr_provider_context'; type DurationUnit = 'm' | 'h' | 'd' | 'w' | 'M'; @@ -58,6 +63,10 @@ interface SloParams { groupBy: string; } +type FetchHistoricalSummaryParams = t.OutputOf< + typeof fetchHistoricalSummaryParamsSchema.props.body +>; + export function SloApiProvider({ getService }: FtrProviderContext) { const es = getService('es'); const supertest = getService('supertest'); @@ -84,6 +93,18 @@ export function SloApiProvider({ getService }: FtrProviderContext) { return response; }, + async fetchHistoricalSummary( + params: FetchHistoricalSummaryParams + ): Promise { + const { body } = await supertest + .post(`/internal/observability/slos/_historical_summary`) + .set('kbn-xsrf', 'foo') + .set('x-elastic-internal-origin', 'foo') + .send(params); + + return body; + }, + async waitForSloToBeDeleted(sloId: string) { if (!sloId) { throw new Error(`sloId is undefined`); diff --git a/x-pack/test_serverless/api_integration/services/svl_cases/api.ts b/x-pack/test_serverless/api_integration/services/svl_cases/api.ts index 8f23eba1ea981..7089655551fbe 100644 --- a/x-pack/test_serverless/api_integration/services/svl_cases/api.ts +++ b/x-pack/test_serverless/api_integration/services/svl_cases/api.ts @@ -141,7 +141,7 @@ export function SvlCasesApiServiceProvider({ getService }: FtrProviderContext) { params: CasePostRequest, expectedHttpCode: number = 200, auth: { user: User; space: string | null } | null = { user: superUser, space: null }, - headers: Record = {} + headers: Record = {} ): Promise { const apiCall = supertest.post(`${CASES_URL}`); diff --git a/x-pack/test_serverless/api_integration/services/transform/api.ts b/x-pack/test_serverless/api_integration/services/transform/api.ts index 39c2d01c6adb1..c865dcec6e8a0 100644 --- a/x-pack/test_serverless/api_integration/services/transform/api.ts +++ b/x-pack/test_serverless/api_integration/services/transform/api.ts @@ -217,7 +217,7 @@ export function TransformAPIProvider({ getService }: FtrProviderContext) { ); const { body, status } = await esSupertest .put(`/_transform/${transformId}${deferValidation ? '?defer_validation=true' : ''}`) - .set(headers) + .set(headers as Record) .send(transformConfig); this.assertResponseStatusCode(200, status, body); } else { diff --git a/x-pack/test_serverless/api_integration/test_suites/common/alerting/helpers/alerting_api_helper.ts b/x-pack/test_serverless/api_integration/test_suites/common/alerting/helpers/alerting_api_helper.ts index bd8c64bed6731..88f3f1a76d3bb 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/alerting/helpers/alerting_api_helper.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/alerting/helpers/alerting_api_helper.ts @@ -6,7 +6,7 @@ */ import moment from 'moment'; -import type { SuperTest, Test } from 'supertest'; +import type { Agent as SuperTestAgent } from 'supertest'; interface CreateEsQueryRuleParams { size: number; @@ -32,7 +32,7 @@ export async function createIndexConnector({ name, indexName, }: { - supertest: SuperTest; + supertest: SuperTestAgent; name: string; indexName: string; }) { @@ -56,7 +56,7 @@ export async function createSlackConnector({ supertest, name, }: { - supertest: SuperTest; + supertest: SuperTestAgent; name: string; }) { const { body } = await supertest @@ -87,7 +87,7 @@ export async function createEsQueryRule({ notifyWhen, enabled = true, }: { - supertest: SuperTest; + supertest: SuperTestAgent; ruleTypeId: string; name: string; params: CreateEsQueryRuleParams; @@ -134,7 +134,7 @@ export async function createAnomalyRule({ ruleTypeId = 'apm.anomaly', params, }: { - supertest: SuperTest; + supertest: SuperTestAgent; name?: string; consumer?: string; actions?: any[]; @@ -184,7 +184,7 @@ export async function createLatencyThresholdRule({ ruleTypeId = 'apm.transaction_duration', params, }: { - supertest: SuperTest; + supertest: SuperTestAgent; name?: string; consumer?: string; actions?: any[]; @@ -233,7 +233,7 @@ export async function createInventoryRule({ ruleTypeId = 'metrics.alert.inventory.threshold', params, }: { - supertest: SuperTest; + supertest: SuperTestAgent; name?: string; consumer?: string; actions?: any[]; @@ -287,7 +287,7 @@ export async function disableRule({ supertest, ruleId, }: { - supertest: SuperTest; + supertest: SuperTestAgent; ruleId: string; }) { const { body } = await supertest @@ -303,7 +303,7 @@ export async function updateEsQueryRule({ ruleId, updates, }: { - supertest: SuperTest; + supertest: SuperTestAgent; ruleId: string; updates: any; }) { @@ -341,7 +341,7 @@ export async function runRule({ supertest, ruleId, }: { - supertest: SuperTest; + supertest: SuperTestAgent; ruleId: string; }) { const response = await supertest @@ -356,7 +356,7 @@ export async function muteRule({ supertest, ruleId, }: { - supertest: SuperTest; + supertest: SuperTestAgent; ruleId: string; }) { const { body } = await supertest @@ -371,7 +371,7 @@ export async function enableRule({ supertest, ruleId, }: { - supertest: SuperTest; + supertest: SuperTestAgent; ruleId: string; }) { const { body } = await supertest @@ -387,7 +387,7 @@ export async function muteAlert({ ruleId, alertId, }: { - supertest: SuperTest; + supertest: SuperTestAgent; ruleId: string; alertId: string; }) { @@ -403,7 +403,7 @@ export async function unmuteRule({ supertest, ruleId, }: { - supertest: SuperTest; + supertest: SuperTestAgent; ruleId: string; }) { const { body } = await supertest @@ -418,7 +418,7 @@ export async function snoozeRule({ supertest, ruleId, }: { - supertest: SuperTest; + supertest: SuperTestAgent; ruleId: string; }) { const { body } = await supertest @@ -443,7 +443,7 @@ export async function findRule({ supertest, ruleId, }: { - supertest: SuperTest; + supertest: SuperTestAgent; ruleId: string; }) { if (!ruleId) { diff --git a/x-pack/test_serverless/api_integration/test_suites/common/alerting/helpers/alerting_wait_for_helpers.ts b/x-pack/test_serverless/api_integration/test_suites/common/alerting/helpers/alerting_wait_for_helpers.ts index 00b746697cd23..55bfa422c5f4b 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/alerting/helpers/alerting_wait_for_helpers.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/alerting/helpers/alerting_wait_for_helpers.ts @@ -6,7 +6,7 @@ */ import pRetry from 'p-retry'; -import type { SuperTest, Test } from 'supertest'; +import type { Agent as SuperTestAgent } from 'supertest'; import type { Client } from '@elastic/elasticsearch'; import type { AggregationsAggregate, @@ -372,7 +372,7 @@ export async function waitForNumRuleRuns({ esClient, testStart, }: { - supertest: SuperTest; + supertest: SuperTestAgent; numOfRuns: number; ruleId: string; esClient: Client; diff --git a/x-pack/test_serverless/api_integration/test_suites/common/index_management/inference_endpoints.ts b/x-pack/test_serverless/api_integration/test_suites/common/index_management/inference_endpoints.ts index ecfcff804d69a..cacf294b4c81a 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/index_management/inference_endpoints.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/index_management/inference_endpoints.ts @@ -19,6 +19,8 @@ export default function ({ getService }: FtrProviderContext) { const service = 'elser'; describe('Inference endpoints', function () { + // test adds new trained model '.elser_model_2_linux-x86_64', but does not clean it. Follow up tests are affected + this.tags(['failsOnMKI']); before(async () => { log.debug(`Creating inference endpoint`); try { diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/apm_api_integration/common/apm_api_supertest.ts b/x-pack/test_serverless/api_integration/test_suites/observability/apm_api_integration/common/apm_api_supertest.ts index ac324b3fda087..62db8c2eddda4 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/apm_api_integration/common/apm_api_supertest.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/apm_api_integration/common/apm_api_supertest.ts @@ -16,7 +16,7 @@ import type { APIEndpoint } from '@kbn/apm-plugin/server'; import { formatRequest } from '@kbn/server-route-repository'; import { InheritedFtrProviderContext } from '../../../../services'; -export function createApmApiClient(st: supertest.SuperTest) { +export function createApmApiClient(st: supertest.Agent) { return async ( options: { type?: 'form-data'; diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/dataset_quality_api_integration/common/dataset_quality_api_supertest.ts b/x-pack/test_serverless/api_integration/test_suites/observability/dataset_quality_api_integration/common/dataset_quality_api_supertest.ts index 2cdb6ec4fd765..f1d746b49ff06 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/dataset_quality_api_integration/common/dataset_quality_api_supertest.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/dataset_quality_api_integration/common/dataset_quality_api_supertest.ts @@ -13,7 +13,7 @@ import type { APIEndpoint } from '@kbn/dataset-quality-plugin/server/routes'; import { formatRequest } from '@kbn/server-route-repository'; import { InheritedFtrProviderContext } from '../../../../services'; -export function createDatasetQualityApiClient(st: supertest.SuperTest) { +export function createDatasetQualityApiClient(st: supertest.Agent) { return async ( options: { type?: 'form-data'; diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/slos/fetch_historical_summary.ts b/x-pack/test_serverless/api_integration/test_suites/observability/slos/fetch_historical_summary.ts new file mode 100644 index 0000000000000..32992e5a3b07b --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/observability/slos/fetch_historical_summary.ts @@ -0,0 +1,132 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { + SLO_DESTINATION_INDEX_NAME, + SLO_DESTINATION_INDEX_PATTERN, +} from '@kbn/slo-plugin/common/constants'; + +import { ALL_VALUE } from '@kbn/slo-schema'; +import moment from 'moment'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const esClient = getService('es'); + const esDeleteAllIndices = getService('esDeleteAllIndices'); + const sloApi = getService('sloApi'); + + const SLO_ID = 'slo-fake-1'; + // Failing: See https://github.com/elastic/kibana/issues/183748 + describe.skip('fetch historical summary', () => { + before(async () => { + const now = moment().startOf('minute'); + const curr = now.clone().subtract(30, 'days'); + const end = now.clone().add(5, 'minutes'); + + const batchOperations = []; + while (curr.isSameOrBefore(end)) { + batchOperations.push([ + { index: { _index: SLO_DESTINATION_INDEX_NAME } }, + { + '@timestamp': curr.toISOString(), + slo: { + id: SLO_ID, + revision: 1, + instanceId: ALL_VALUE, + numerator: 90, + denominator: 100, + isGoodSlice: 1, + groupings: {}, + }, + }, + ]); + curr.add(1, 'minute'); + } + + await esClient.bulk({ + index: SLO_DESTINATION_INDEX_NAME, + operations: batchOperations.flat(), + refresh: 'wait_for', + }); + + await esClient.indices.refresh({ index: SLO_DESTINATION_INDEX_NAME }); + }); + + after(async () => { + await esDeleteAllIndices(SLO_DESTINATION_INDEX_PATTERN); + }); + + it('computes the historical summary for a rolling occurrences SLO', async () => { + const response = await sloApi.fetchHistoricalSummary({ + list: [ + { + sloId: SLO_ID, + instanceId: ALL_VALUE, + timeWindow: { + duration: '7d', + type: 'rolling', + }, + budgetingMethod: 'occurrences', + objective: { + target: 0.9, + }, + groupBy: ALL_VALUE, + revision: 1, + }, + ], + }); + expect(response[0].sloId).to.eql(SLO_ID); + expect(response[0].instanceId).to.eql(ALL_VALUE); + expect(response[0].data).to.have.length(168); // 7 days * 24 hours/day * 1 bucket/hour + const last = response[0].data.pop(); + expect(last?.errorBudget).to.eql({ + consumed: 1, + initial: 0.1, + isEstimated: false, + remaining: 0, + }); + expect(last?.sliValue).to.eql(0.9); + expect(last?.status).to.eql('HEALTHY'); + }); + + it('computes the historical summary for a rolling timeslices SLO', async () => { + const response = await sloApi.fetchHistoricalSummary({ + list: [ + { + sloId: SLO_ID, + instanceId: ALL_VALUE, + timeWindow: { + duration: '7d', + type: 'rolling', + }, + budgetingMethod: 'timeslices', + objective: { + target: 0.9, + timesliceTarget: 0.8, + timesliceWindow: '1m', + }, + groupBy: ALL_VALUE, + revision: 1, + }, + ], + }); + expect(response[0].sloId).to.eql(SLO_ID); + expect(response[0].instanceId).to.eql(ALL_VALUE); + expect(response[0].data).to.have.length(168); // 7 days * 24 hours/day * 1 bucket/hour + const last = response[0].data.pop(); + expect(last?.errorBudget).to.eql({ + consumed: 0, + initial: 0.1, + isEstimated: false, + remaining: 1, + }); + expect(last?.sliValue).to.eql(1); + expect(last?.status).to.eql('HEALTHY'); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/slos/index.ts b/x-pack/test_serverless/api_integration/test_suites/observability/slos/index.ts index 77d2b169fc7aa..8df59e6f3b624 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/slos/index.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/slos/index.ts @@ -10,5 +10,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { describe('SLOs', function () { loadTestFile(require.resolve('./create_slo')); loadTestFile(require.resolve('./delete_slo')); + loadTestFile(require.resolve('./fetch_historical_summary')); }); } diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/synthetics/synthetics_enablement.ts b/x-pack/test_serverless/api_integration/test_suites/observability/synthetics/synthetics_enablement.ts index fae09f97cf2a2..9925c00fe5748 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/synthetics/synthetics_enablement.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/synthetics/synthetics_enablement.ts @@ -65,7 +65,7 @@ export default function ({ getService }: FtrProviderContext) { async function enablementPut(role: RoleName = 'admin', expectedStatus: number = 200) { return supertestWithoutAuth .put(SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT) - .set(internalRequestHeader) + .set(internalRequestHeader as unknown as Record) .set(await svlUserManager.getApiCredentialsForRole(role)) .expect(expectedStatus); } @@ -73,7 +73,7 @@ export default function ({ getService }: FtrProviderContext) { async function enablementDelete(role: RoleName = 'admin', expectedStatus: number = 200) { return supertestWithoutAuth .delete(SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT) - .set(internalRequestHeader) + .set(internalRequestHeader as unknown as Record) .set(await svlUserManager.getApiCredentialsForRole(role)) .expect(expectedStatus); } diff --git a/x-pack/test_serverless/functional/page_objects/index.ts b/x-pack/test_serverless/functional/page_objects/index.ts index ebc85de7d332c..f1604d48508e2 100644 --- a/x-pack/test_serverless/functional/page_objects/index.ts +++ b/x-pack/test_serverless/functional/page_objects/index.ts @@ -21,7 +21,6 @@ import { SvlRuleDetailsPageProvider } from './svl_rule_details_ui_page'; import { SvlSearchConnectorsPageProvider } from './svl_search_connectors_page'; import { SvlManagementPageProvider } from './svl_management_page'; import { SvlIngestPipelines } from './svl_ingest_pipelines'; -import { SvlPlaygroundPageProvider } from './svl_playground_page'; export const pageObjects = { ...xpackFunctionalPageObjects, @@ -39,5 +38,4 @@ export const pageObjects = { svlRuleDetailsUI: SvlRuleDetailsPageProvider, svlManagementPage: SvlManagementPageProvider, svlIngestPipelines: SvlIngestPipelines, - svlPlaygroundUI: SvlPlaygroundPageProvider, }; diff --git a/x-pack/test_serverless/functional/page_objects/svl_playground_page.ts b/x-pack/test_serverless/functional/page_objects/svl_playground_page.ts deleted file mode 100644 index e0c26c031eb50..0000000000000 --- a/x-pack/test_serverless/functional/page_objects/svl_playground_page.ts +++ /dev/null @@ -1,26 +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 { FtrProviderContext } from '../ftr_provider_context'; - -export function SvlPlaygroundPageProvider({ getService }: FtrProviderContext) { - const testSubjects = getService('testSubjects'); - return { - PlaygrounStartChatPage: { - async expectPlaygroundStartChatPageComponentsToExist() { - await testSubjects.existOrFail('chat-playground-home-page-title'); - await testSubjects.existOrFail('selectIndicesChatPanel'); - await testSubjects.existOrFail('startChatButton'); - }, - - async expectPlaygroundHeaderComponentsToExist() { - await testSubjects.existOrFail('playground-header-actions'); - await testSubjects.existOrFail('playground-documentation-link'); - }, - }, - }; -} diff --git a/x-pack/test_serverless/functional/test_suites/common/management/index_management/index.ts b/x-pack/test_serverless/functional/test_suites/common/management/index_management/index.ts index 0df628a2ba587..4d69adb9b6bed 100644 --- a/x-pack/test_serverless/functional/test_suites/common/management/index_management/index.ts +++ b/x-pack/test_serverless/functional/test_suites/common/management/index_management/index.ts @@ -15,5 +15,6 @@ export default ({ loadTestFile }: FtrProviderContext) => { loadTestFile(require.resolve('./enrich_policies')); loadTestFile(require.resolve('./index_templates')); loadTestFile(require.resolve('./indices')); + loadTestFile(require.resolve('./index_detail')); }); }; diff --git a/x-pack/test_serverless/functional/test_suites/common/management/index_management/index_detail.ts b/x-pack/test_serverless/functional/test_suites/common/management/index_management/index_detail.ts new file mode 100644 index 0000000000000..b4e9aad21d2c6 --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/common/management/index_management/index_detail.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default ({ getPageObjects, getService }: FtrProviderContext) => { + const pageObjects = getPageObjects(['svlCommonPage', 'common', 'indexManagement', 'header']); + const browser = getService('browser'); + const security = getService('security'); + const testIndexName = `index-ftr-test-${Math.random()}`; + describe('Index Details ', function () { + before(async () => { + await security.testUser.setRoles(['index_management_user']); + await pageObjects.svlCommonPage.loginAsAdmin(); + await pageObjects.common.navigateToApp('indexManagement'); + // Navigate to the indices tab + await pageObjects.indexManagement.changeTabs('indicesTab'); + await pageObjects.header.waitUntilLoadingHasFinished(); + }); + + it('renders the indices tab', async () => { + const url = await browser.getCurrentUrl(); + expect(url).to.contain(`/indices`); + }); + it('can create an index', async () => { + await pageObjects.indexManagement.clickCreateIndexButton(); + await pageObjects.indexManagement.setCreateIndexName(testIndexName); + await pageObjects.indexManagement.clickCreateIndexSaveButton(); + await pageObjects.indexManagement.expectIndexToExist(testIndexName); + }); + it('index with no documents', async () => { + await pageObjects.indexManagement.indexDetailsPage.openIndexDetailsPage(0); + await pageObjects.indexManagement.indexDetailsPage.expectIndexDetailsPageIsLoaded(); + }); + }); +}; diff --git a/x-pack/test_serverless/functional/test_suites/common/management/index_management/indices.ts b/x-pack/test_serverless/functional/test_suites/common/management/index_management/indices.ts index c0994de088513..fe5938109d7b8 100644 --- a/x-pack/test_serverless/functional/test_suites/common/management/index_management/indices.ts +++ b/x-pack/test_serverless/functional/test_suites/common/management/index_management/indices.ts @@ -22,17 +22,25 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await pageObjects.indexManagement.changeTabs('indicesTab'); await pageObjects.header.waitUntilLoadingHasFinished(); }); - + const testIndexName = `index-ftr-test-${Math.random()}`; it('renders the indices tab', async () => { const url = await browser.getCurrentUrl(); expect(url).to.contain(`/indices`); }); it('can create an index', async () => { - const testIndexName = `index-ftr-test-${Math.random()}`; await pageObjects.indexManagement.clickCreateIndexButton(); await pageObjects.indexManagement.setCreateIndexName(testIndexName); await pageObjects.indexManagement.clickCreateIndexSaveButton(); await pageObjects.indexManagement.expectIndexToExist(testIndexName); }); + it('can manage index', async () => { + await pageObjects.indexManagement.selectIndex(testIndexName); + await pageObjects.indexManagement.clickManageButton(); + await pageObjects.indexManagement.contextMenuIsVisible(); + }); + it('can delete index', async () => { + await pageObjects.indexManagement.confirmDeleteModalIsVisible(); + await pageObjects.indexManagement.expectIndexIsDeleted(testIndexName); + }); }); }; diff --git a/x-pack/test_serverless/functional/test_suites/observability/dataset_quality/dataset_quality_flyout.ts b/x-pack/test_serverless/functional/test_suites/observability/dataset_quality/dataset_quality_flyout.ts index 9688c177c2e73..2f2c40932ceaf 100644 --- a/x-pack/test_serverless/functional/test_suites/observability/dataset_quality/dataset_quality_flyout.ts +++ b/x-pack/test_serverless/functional/test_suites/observability/dataset_quality/dataset_quality_flyout.ts @@ -31,7 +31,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const to = '2024-01-01T12:00:00.000Z'; const excludeKeysFromServerless = ['size']; // https://github.com/elastic/kibana/issues/178954 - describe('Dataset quality flyout', function () { + // FLAKY: https://github.com/elastic/kibana/issues/183771 + describe.skip('Dataset quality flyout', function () { this.tags(['failsOnMKI']); // Failing https://github.com/elastic/kibana/issues/183495 before(async () => { diff --git a/x-pack/test_serverless/functional/test_suites/search/playground_overview.ts b/x-pack/test_serverless/functional/test_suites/search/playground_overview.ts index 17d9d81c1014c..0a75db1545440 100644 --- a/x-pack/test_serverless/functional/test_suites/search/playground_overview.ts +++ b/x-pack/test_serverless/functional/test_suites/search/playground_overview.ts @@ -5,23 +5,148 @@ * 2.0. */ +import type SuperTest from 'supertest'; +import { testHasEmbeddedConsole } from './embedded_console'; import { FtrProviderContext } from '../../ftr_provider_context'; +import { RoleCredentials } from '../../../shared/services'; + +const indexName = 'basic_index'; +const esArchiveIndex = 'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'; +async function createOpenAIConnector({ + supertest, + requestHeader = {}, + apiKeyHeader = {}, +}: { + supertest: SuperTest.Agent; + requestHeader?: Record; + apiKeyHeader?: Record; +}): Promise<() => Promise> { + const config = { + apiProvider: 'OpenAI', + defaultModel: 'gpt-4', + apiUrl: 'http://localhost:3002', + }; + + const connector: { id: string } | undefined = ( + await supertest + .post('/api/actions/connector') + .set(requestHeader) + .set(apiKeyHeader) + .send({ + name: 'test Open AI', + connector_type_id: '.gen-ai', + config, + secrets: { + apiKey: 'genAiApiKey', + }, + }) + .expect(200) + ).body; + + return async () => { + if (connector) { + await supertest + .delete(`/api/actions/connector/${connector.id}`) + .set(requestHeader) + .set(apiKeyHeader) + .expect(204); + } + }; +} + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const pageObjects = getPageObjects(['svlCommonPage', 'svlCommonNavigation', 'searchPlayground']); + const svlCommonApi = getService('svlCommonApi'); + const svlUserManager = getService('svlUserManager'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + const esArchiver = getService('esArchiver'); + const createIndex = async () => await esArchiver.load(esArchiveIndex); + let roleAuthc: RoleCredentials; + + describe('Serverless Playground Overview', () => { + let removeOpenAIConnector: () => Promise; + let createConnector: () => Promise; -export default function ({ getPageObjects }: FtrProviderContext) { - const pageObjects = getPageObjects(['svlCommonPage', 'svlCommonNavigation', 'svlPlaygroundUI']); - describe('Playground', function () { before(async () => { await pageObjects.svlCommonPage.login(); - await pageObjects.svlCommonNavigation.sidenav.clickLink({ deepLinkId: 'searchPlayground' }); + await pageObjects.svlCommonNavigation.sidenav.clickLink({ + deepLinkId: 'searchPlayground', + }); + + const requestHeader = svlCommonApi.getInternalRequestHeader(); + roleAuthc = await svlUserManager.createApiKeyForRole('admin'); + createConnector = async () => { + removeOpenAIConnector = await createOpenAIConnector({ + supertest: supertestWithoutAuth, + requestHeader, + apiKeyHeader: roleAuthc.apiKeyHeader, + }); + }; }); after(async () => { + await removeOpenAIConnector?.(); + await esArchiver.unload(esArchiveIndex); + await svlUserManager.invalidateApiKeyForRole(roleAuthc); await pageObjects.svlCommonPage.forceLogout(); }); - it('playground app is loaded', async () => { - await pageObjects.svlPlaygroundUI.PlaygrounStartChatPage.expectPlaygroundStartChatPageComponentsToExist(); - await pageObjects.svlPlaygroundUI.PlaygrounStartChatPage.expectPlaygroundHeaderComponentsToExist(); + describe('start chat page', () => { + it('playground app is loaded', async () => { + await pageObjects.searchPlayground.PlaygroundStartChatPage.expectPlaygroundStartChatPageComponentsToExist(); + await pageObjects.searchPlayground.PlaygroundStartChatPage.expectPlaygroundHeaderComponentsToExist(); + }); + + it('show no index callout', async () => { + await pageObjects.searchPlayground.PlaygroundStartChatPage.expectNoIndexCalloutExists(); + await pageObjects.searchPlayground.PlaygroundStartChatPage.expectCreateIndexButtonToMissed(); + }); + + it('hide no index callout when index added', async () => { + await createIndex(); + await pageObjects.searchPlayground.PlaygroundStartChatPage.expectSelectIndex(indexName); + }); + + it('show add connector button', async () => { + await pageObjects.searchPlayground.PlaygroundStartChatPage.expectAddConnectorButtonExists(); + }); + + it('click add connector button opens connector flyout', async () => { + await pageObjects.searchPlayground.PlaygroundStartChatPage.expectOpenConnectorPagePlayground(); + }); + + it('hide gen ai panel when connector exists', async () => { + await pageObjects.searchPlayground.PlaygroundStartChatPage.expectHideGenAIPanelConnector( + createConnector + ); + }); + + it('show chat page', async () => { + await pageObjects.searchPlayground.PlaygroundStartChatPage.expectSelectIndex(indexName); + await pageObjects.searchPlayground.PlaygroundStartChatPage.expectToStartChatPage(); + }); + }); + + describe('chat page', () => { + it('chat works', async () => { + await pageObjects.searchPlayground.PlaygroundChatPage.expectChatWorks(); + }); + + it('open view code', async () => { + await pageObjects.searchPlayground.PlaygroundChatPage.expectOpenViewCode(); + }); + + it('show fields and code in view query', async () => { + await pageObjects.searchPlayground.PlaygroundChatPage.expectViewQueryHasFields(); + }); + + it('show edit context', async () => { + await pageObjects.searchPlayground.PlaygroundChatPage.expectEditContextOpens(); + }); + }); + + it('has embedded console', async () => { + await testHasEmbeddedConsole(pageObjects); }); }); } diff --git a/x-pack/test_serverless/shared/lib/object_remover.ts b/x-pack/test_serverless/shared/lib/object_remover.ts index ad029ca579cbd..ef43c70d0ee49 100644 --- a/x-pack/test_serverless/shared/lib/object_remover.ts +++ b/x-pack/test_serverless/shared/lib/object_remover.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SuperTest, Test } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { getUrlPathPrefixForSpace } from './space_path_prefix'; @@ -18,10 +18,10 @@ interface ObjectToRemove { } export class ObjectRemover { - private readonly supertest: SuperTest; + private readonly supertest: SuperTestAgent; private objectsToRemove: ObjectToRemove[] = []; - constructor(supertest: SuperTest) { + constructor(supertest: SuperTestAgent) { this.supertest = supertest; } @@ -60,7 +60,7 @@ export class ObjectRemover { } interface DeleteObjectParams { - supertest: SuperTest; + supertest: SuperTestAgent; url: string; plugin: string; } diff --git a/x-pack/test_serverless/shared/services/bsearch_secure.ts b/x-pack/test_serverless/shared/services/bsearch_secure.ts index 2493c1d76e09c..14a373ab99686 100644 --- a/x-pack/test_serverless/shared/services/bsearch_secure.ts +++ b/x-pack/test_serverless/shared/services/bsearch_secure.ts @@ -25,7 +25,7 @@ const parseBfetchResponse = (resp: request.Response): Array> }; interface SendOptions { - supertestWithoutAuth: SuperTest.SuperTest; + supertestWithoutAuth: SuperTest.Agent; apiKeyHeader: { Authorization: string }; referer?: string; kibanaVersion?: string; diff --git a/yarn.lock b/yarn.lock index e35fbacacef73..b32d917f64ce6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1735,10 +1735,10 @@ resolved "https://registry.yarnpkg.com/@elastic/eslint-plugin-eui/-/eslint-plugin-eui-0.0.2.tgz#56b9ef03984a05cc213772ae3713ea8ef47b0314" integrity sha512-IoxURM5zraoQ7C8f+mJb9HYSENiZGgRVcG4tLQxE61yHNNRDXtGDWTZh8N1KIHcsqN1CEPETjuzBXkJYF/fDiQ== -"@elastic/eui@94.3.0": - version "94.3.0" - resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-94.3.0.tgz#77c07701128b6443e2ea55e2462ef0ccadd64582" - integrity sha512-qJTLrQYe11MPTX+8AqifJVYLDyO8VqdFWqPVJRYel11l/FvJOqyQi50x+xQK8I7h73TF50xywtUHCfhfkqpbYg== +"@elastic/eui@94.5.0-backport.1": + version "94.5.0-backport.1" + resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-94.5.0-backport.1.tgz#b26b06c9d4823b80b6c2a4457edfd6cd0cb8ebad" + integrity sha512-K6xVQYZjhdZyHNANw4H45ONw2pbZEJtaKr3Aul+dIQmYSRs0NgZbZyjt5fiLMaHJz2EFIpDEZMFccW6l0Aiukg== dependencies: "@hello-pangea/dnd" "^16.6.0" "@types/lodash" "^4.14.202" @@ -4583,6 +4583,10 @@ version "0.0.0" uid "" +"@kbn/entities-schema@link:x-pack/packages/kbn-entities-schema": + version "0.0.0" + uid "" + "@kbn/error-boundary-example-plugin@link:examples/error_boundary": version "0.0.0" uid "" @@ -9555,10 +9559,10 @@ dependencies: "@types/node" "*" -"@types/cookiejar@*": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@types/cookiejar/-/cookiejar-2.1.2.tgz#66ad9331f63fe8a3d3d9d8c6e3906dd10f6446e8" - integrity sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog== +"@types/cookiejar@^2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@types/cookiejar/-/cookiejar-2.1.5.tgz#14a3e83fa641beb169a2dd8422d91c3c345a9a78" + integrity sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q== "@types/cytoscape@^3.14.0": version "3.14.0" @@ -10163,6 +10167,11 @@ resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-1.0.2.tgz#e2ce9d83a613bacf284c7be7d491945e39e1f8e9" integrity sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA== +"@types/methods@^1.1.4": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@types/methods/-/methods-1.1.4.tgz#d3b7ac30ac47c91054ea951ce9eed07b1051e547" + integrity sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ== + "@types/micromatch@^4.0.2": version "4.0.2" resolved "https://registry.yarnpkg.com/@types/micromatch/-/micromatch-4.0.2.tgz#ce29c8b166a73bf980a5727b1e4a4d099965151d" @@ -10268,7 +10277,7 @@ dependencies: "@types/node" "*" -"@types/node@*", "@types/node@20.10.5", "@types/node@>= 8", "@types/node@>=12.12.47", "@types/node@>=13.7.0", "@types/node@>=18.0.0", "@types/node@^14.0.10 || ^16.0.0", "@types/node@^14.14.20 || ^16.0.0", "@types/node@^18.0.0", "@types/node@^18.11.18", "@types/node@^18.17.5": +"@types/node@*", "@types/node@20.10.5", "@types/node@>= 8", "@types/node@>=12.12.47", "@types/node@>=13.7.0", "@types/node@>=18.0.0", "@types/node@^14.0.10 || ^16.0.0", "@types/node@^14.14.20 || ^16.0.0", "@types/node@^18.0.0", "@types/node@^18.11.18": version "20.10.5" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.5.tgz#47ad460b514096b7ed63a1dae26fad0914ed3ab2" integrity sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw== @@ -10701,20 +10710,22 @@ resolved "https://registry.yarnpkg.com/@types/stylis/-/stylis-4.2.0.tgz#199a3f473f0c3a6f6e4e1b17cdbc967f274bdc6b" integrity sha512-n4sx2bqL0mW1tvDf/loQ+aMX7GQD3lc3fkCMC55VFNDu/vBOabO+LTIeXKM14xK0ppk5TUGcWRjiSpIlUpghKw== -"@types/superagent@*": - version "3.8.4" - resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-3.8.4.tgz#24a5973c7d1a9c024b4bbda742a79267c33fb86a" - integrity sha512-Dnh0Iw6NO55z1beXvlsvUrfk4cd9eL2nuTmUk+rAhSVCk10PGGFbqCCTwbau9D0d2W3DITiXl4z8VCqppGkMPQ== +"@types/superagent@^8.1.0": + version "8.1.7" + resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-8.1.7.tgz#1153819ed4db34427409a1cc58f3e2f13eeec862" + integrity sha512-NmIsd0Yj4DDhftfWvvAku482PZum4DBW7U51OvS8gvOkDDY0WT1jsVyDV3hK+vplrsYw8oDwi9QxOM7U68iwww== dependencies: - "@types/cookiejar" "*" + "@types/cookiejar" "^2.1.5" + "@types/methods" "^1.1.4" "@types/node" "*" -"@types/supertest@^2.0.12": - version "2.0.12" - resolved "https://registry.yarnpkg.com/@types/supertest/-/supertest-2.0.12.tgz#ddb4a0568597c9aadff8dbec5b2e8fddbe8692fc" - integrity sha512-X3HPWTwXRerBZS7Mo1k6vMVR1Z6zmJcDVn5O/31whe0tnjE4te6ZJSJGq1RiqHPjzPdMTfjCFogDJmwng9xHaQ== +"@types/supertest@^6.0.2": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@types/supertest/-/supertest-6.0.2.tgz#2af1c466456aaf82c7c6106c6b5cbd73a5e86588" + integrity sha512-137ypx2lk/wTQbW6An6safu9hXmajAifU/s7szAHLN/FeIm5w7yR0Wkl9fdJMRSHwOn4HLAI0DaB2TOORuhPDg== dependencies: - "@types/superagent" "*" + "@types/methods" "^1.1.4" + "@types/superagent" "^8.1.0" "@types/tapable@^1", "@types/tapable@^1.0.5", "@types/tapable@^1.0.6": version "1.0.6" @@ -12246,12 +12257,12 @@ axe-core@^4.9.0: resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.9.0.tgz#b18971494551ab39d4ff5f7d4c6411bd20cc7c2a" integrity sha512-H5orY+M2Fr56DWmMFpMrq5Ge93qjNdPVqzBv5gWK3aD1OvjBEJlEzxf09z93dGVQeI0LiW+aCMIx1QtShC/zUw== -axios@1.6.3, axios@^1.0.0, axios@^1.3.4, axios@^1.6.0, axios@^1.6.7: - version "1.6.3" - resolved "https://registry.npmjs.org/axios/-/axios-1.6.3.tgz#7f50f23b3aa246eff43c54834272346c396613f4" - integrity sha512-fWyNdeawGam70jXSVlKl+SUNVcL6j6W79CuSIPfi6HnDUmSCH6gyUys/HrqHeA/wU0Az41rRgean494d0Jb+ww== +axios@^1.0.0, axios@^1.3.4, axios@^1.6.0, axios@^1.6.7, axios@^1.6.8: + version "1.6.8" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66" + integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ== dependencies: - follow-redirects "^1.15.0" + follow-redirects "^1.15.6" form-data "^4.0.0" proxy-from-env "^1.1.0" @@ -14692,14 +14703,13 @@ cypress-recurse@^1.35.2: dependencies: humanize-duration "^3.27.3" -cypress@13.6.2: - version "13.6.2" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.6.2.tgz#c70df09db0a45063298b3cecba2fa21109768e08" - integrity sha512-TW3bGdPU4BrfvMQYv1z3oMqj71YI4AlgJgnrycicmPZAXtvywVFZW9DAToshO65D97rCWfG/kqMFsYB6Kp91gQ== +cypress@13.6.3: + version "13.6.3" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.6.3.tgz#54f03ca07ee56b2bc18211e7bd32abd2533982ba" + integrity sha512-d/pZvgwjAyZsoyJ3FOsJT5lDsqnxQ/clMqnNc++rkHjbkkiF2h9s0JsZSyyH4QXhVFW3zPFg82jD25roFLOdZA== dependencies: "@cypress/request" "^3.0.0" "@cypress/xvfb" "^1.2.4" - "@types/node" "^18.17.5" "@types/sinonjs__fake-timers" "8.1.1" "@types/sizzle" "^2.3.2" arch "^2.2.0" @@ -17589,10 +17599,10 @@ focus-lock@^0.11.6: dependencies: tslib "^2.0.3" -follow-redirects@1.15.2, follow-redirects@^1.0.0, follow-redirects@^1.15.0: - version "1.15.2" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" - integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== +follow-redirects@^1.0.0, follow-redirects@^1.15.6: + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== font-awesome@4.7.0: version "4.7.0" @@ -17726,15 +17736,14 @@ formdata-polyfill@^4.0.10: dependencies: fetch-blob "^3.1.2" -formidable@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/formidable/-/formidable-2.1.2.tgz#fa973a2bec150e4ce7cac15589d7a25fc30ebd89" - integrity sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g== +formidable@^3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/formidable/-/formidable-3.5.1.tgz#9360a23a656f261207868b1484624c4c8d06ee1a" + integrity sha512-WJWKelbRHN41m5dumb0/k8TeAx7Id/y3a+Z7QfhxP/htI9Js5zYaEDtG8uMgG0vM0lOlqnmjE99/kfpOYi/0Og== dependencies: dezalgo "^1.0.4" hexoid "^1.0.0" once "^1.4.0" - qs "^6.11.0" formik@^2.4.5: version "2.4.5" @@ -27655,7 +27664,7 @@ semver@7.5.4: dependencies: lru-cache "^6.0.0" -semver@7.6.0, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.0, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4: +semver@7.6.0, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.5.0, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4: version "7.6.0" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== @@ -28745,7 +28754,7 @@ string-replace-loader@^2.2.0: loader-utils "^1.2.3" schema-utils "^1.0.0" -"string-width-cjs@npm:string-width@^4.2.0": +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -28763,15 +28772,6 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -28881,7 +28881,7 @@ stringify-object@^3.2.1: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -28895,13 +28895,6 @@ strip-ansi@^3.0.0, strip-ansi@^3.0.1: dependencies: ansi-regex "^2.0.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -29098,21 +29091,20 @@ stylus-lookup@^5.0.1: dependencies: commander "^10.0.1" -superagent@^8.0.5, superagent@^8.1.2: - version "8.1.2" - resolved "https://registry.yarnpkg.com/superagent/-/superagent-8.1.2.tgz#03cb7da3ec8b32472c9d20f6c2a57c7f3765f30b" - integrity sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA== +superagent@^9.0.1, superagent@^9.0.2: + version "9.0.2" + resolved "https://registry.yarnpkg.com/superagent/-/superagent-9.0.2.tgz#a18799473fc57557289d6b63960610e358bdebc1" + integrity sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w== dependencies: component-emitter "^1.3.0" cookiejar "^2.1.4" debug "^4.3.4" fast-safe-stringify "^2.1.1" form-data "^4.0.0" - formidable "^2.1.2" + formidable "^3.5.1" methods "^1.1.2" mime "2.6.0" qs "^6.11.0" - semver "^7.3.8" supercluster@^8.0.1: version "8.0.1" @@ -29128,13 +29120,13 @@ superjson@^1.10.0: dependencies: copy-anything "^3.0.2" -supertest@^6.3.3: - version "6.3.3" - resolved "https://registry.yarnpkg.com/supertest/-/supertest-6.3.3.tgz#42f4da199fee656106fd422c094cf6c9578141db" - integrity sha512-EMCG6G8gDu5qEqRQ3JjjPs6+FYT1a7Hv5ApHvtSghmOFJYtsU5S+pSb6Y2EUeCEY3CmEL3mmQ8YWlPOzQomabA== +supertest@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/supertest/-/supertest-7.0.0.tgz#cac53b3d6872a0b317980b2b0cfa820f09cd7634" + integrity sha512-qlsr7fIC0lSddmA3tzojvzubYxvlGtzumcdHgPwbFWMISQwL22MhM2Y3LNt+6w9Yyx7559VW5ab70dgphm8qQA== dependencies: methods "^1.1.2" - superagent "^8.0.5" + superagent "^9.0.1" supports-color@8.1.1, supports-color@^8.0.0, supports-color@^8.1.1: version "8.1.1" @@ -31775,7 +31767,7 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -31801,15 +31793,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"