diff --git a/.buildkite/ftr_configs.yml b/.buildkite/ftr_configs.yml index 4adb920c81e43..1400d1fdee4cf 100644 --- a/.buildkite/ftr_configs.yml +++ b/.buildkite/ftr_configs.yml @@ -359,4 +359,5 @@ enabled: - x-pack/performance/journeys/ecommerce_dashboard_saved_search_only.ts - x-pack/performance/journeys/ecommerce_dashboard_tsvb_gauge_only.ts - x-pack/performance/journeys/dashboard_listing_page.ts + - x-pack/performance/journeys/cloud_security_dashboard.ts - x-pack/test/custom_branding/config.ts diff --git a/.buildkite/pipelines/code_coverage/daily.yml b/.buildkite/pipelines/code_coverage/daily.yml index a2b73329b4b01..ab6fdfc29ad87 100644 --- a/.buildkite/pipelines/code_coverage/daily.yml +++ b/.buildkite/pipelines/code_coverage/daily.yml @@ -20,7 +20,7 @@ steps: - command: .buildkite/scripts/steps/code_coverage/ingest.sh label: 'Merge and Ingest' agents: - queue: c2-16 + queue: n2-4-spot depends_on: - jest - jest-integration diff --git a/.buildkite/pipelines/scalability/api_capacity_testing_daily.yml b/.buildkite/pipelines/scalability/api_capacity_testing_daily.yml new file mode 100644 index 0000000000000..35fba7584bd4f --- /dev/null +++ b/.buildkite/pipelines/scalability/api_capacity_testing_daily.yml @@ -0,0 +1,38 @@ +steps: + - label: 'Pre-Build' + command: .buildkite/scripts/lifecycle/pre_build.sh + agents: + queue: kibana-default + + - wait + + - label: 'Build Kibana Distribution and Plugins' + command: .buildkite/scripts/steps/build_kibana.sh + agents: + queue: n2-16-spot + key: build + if: "build.env('KIBANA_BUILD_ID') == null || build.env('KIBANA_BUILD_ID') == ''" + timeout_in_minutes: 60 + retry: + automatic: + - exit_status: '-1' + limit: 3 + + - label: ':kibana: APIs Capacity Tests' + command: .buildkite/scripts/steps/scalability/api_capacity_testing.sh + agents: + queue: kb-static-scalability + depends_on: build + timeout_in_minutes: 90 + retry: + automatic: + - exit_status: '*' + limit: 1 + + - wait: ~ + continue_on_failure: true + + - label: 'Post-Build' + command: .buildkite/scripts/lifecycle/post_build.sh + agents: + queue: kibana-default diff --git a/.buildkite/scripts/steps/functional/apm_cypress.sh b/.buildkite/scripts/steps/functional/apm_cypress.sh index ff14df87377cd..987d9de577c8b 100755 --- a/.buildkite/scripts/steps/functional/apm_cypress.sh +++ b/.buildkite/scripts/steps/functional/apm_cypress.sh @@ -12,8 +12,8 @@ APM_CYPRESS_RECORD_KEY="$(retry 5 5 vault read -field=CYPRESS_RECORD_KEY secret/ export JOB=kibana-apm-cypress IS_FLAKY_TEST_RUNNER=${CLI_COUNT:-0} -# Disable parallel tests and dashboard recording when running them in the flaky test runner -if [[ "$IS_FLAKY_TEST_RUNNER" -ne 1 ]]; then +#Enabling cypress dashboard recording when PR is labeled with `apm:cypress-record` and we are not using the flaky test runner +if [[ "$IS_FLAKY_TEST_RUNNER" -ne 1 ]] && is_pr_with_label "apm:cypress-record"; then CYPRESS_ARGS="--record --key "$APM_CYPRESS_RECORD_KEY" --parallel --ci-build-id "${BUILDKITE_BUILD_ID}"" else CYPRESS_ARGS="" diff --git a/.buildkite/scripts/steps/scalability/api_capacity_testing.sh b/.buildkite/scripts/steps/scalability/api_capacity_testing.sh new file mode 100755 index 0000000000000..7dbd103eb1531 --- /dev/null +++ b/.buildkite/scripts/steps/scalability/api_capacity_testing.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +set -euo pipefail + +source .buildkite/scripts/common/util.sh + +source .buildkite/scripts/steps/scalability/util.sh + +bootstrap_kibana + +KIBANA_LOAD_TESTING_DIR="${KIBANA_DIR}/kibana-load-testing" +# These tests are running on static workers so we must delete previous build, load runner and scalability artifacts +rm -rf "${KIBANA_BUILD_LOCATION}" +rm -rf "${KIBANA_LOAD_TESTING_DIR}" + +echo "--- Download the build artifacts" +.buildkite/scripts/download_build_artifacts.sh + +echo "--- Clone kibana-load-testing repo and compile project" +mkdir -p "${KIBANA_LOAD_TESTING_DIR}" && cd "${KIBANA_LOAD_TESTING_DIR}" +checkout_and_compile_load_runner + +echo "--- Run single apis capacity tests" +cd "$KIBANA_DIR" +node scripts/run_scalability --kibana-install-dir "$KIBANA_BUILD_LOCATION" --journey-path "x-pack/test/scalability/apis" + +echo "--- Upload test results" +upload_test_results diff --git a/.buildkite/scripts/steps/scalability/benchmarking.sh b/.buildkite/scripts/steps/scalability/benchmarking.sh index 9ad62d2dbddb8..39acf4203af37 100755 --- a/.buildkite/scripts/steps/scalability/benchmarking.sh +++ b/.buildkite/scripts/steps/scalability/benchmarking.sh @@ -4,9 +4,9 @@ set -euo pipefail source .buildkite/scripts/common/util.sh -#.buildkite/scripts/bootstrap.sh -echo "--- yarn kbn reset && yarn kbn bootstrap" -yarn kbn reset && yarn kbn bootstrap +source .buildkite/scripts/steps/scalability/util.sh + +bootstrap_kibana GCS_BUCKET="gs://kibana-performance/scalability-tests" GCS_ARTIFACTS_REL="gcs_artifacts" @@ -37,51 +37,20 @@ download_artifacts() { tar -xzf "${LATEST_RUN_ARTIFACTS_DIR}/scalability_traces.tar.gz" } -checkout_and_compile_load_runner() { - mkdir -p "${KIBANA_LOAD_TESTING_DIR}" && cd "${KIBANA_LOAD_TESTING_DIR}" - - if [[ ! -d .git ]]; then - git init - git remote add origin https://github.com/elastic/kibana-load-testing.git - fi - git fetch origin --depth 1 "main" - git reset --hard FETCH_HEAD - - KIBANA_LOAD_TESTING_GIT_COMMIT="$(git rev-parse HEAD)" - export KIBANA_LOAD_TESTING_GIT_COMMIT - - mvn -q test-compile - echo "Set 'GATLING_PROJECT_PATH' env var for ScalabilityTestRunner" - export GATLING_PROJECT_PATH="$(pwd)" -} - -upload_test_results() { - cd "${KIBANA_DIR}" - echo "Upload server logs as build artifacts" - tar -czf server-logs.tar.gz data/ftr_servers_logs/**/* - buildkite-agent artifact upload server-logs.tar.gz - echo "--- Upload Gatling reports as build artifacts" - tar -czf "scalability_test_report.tar.gz" --exclude=simulation.log -C kibana-load-testing/target gatling - buildkite-agent artifact upload "scalability_test_report.tar.gz" - cd "${LATEST_RUN_ARTIFACTS_DIR}" - echo "Upload scalability traces as build artifacts" - buildkite-agent artifact upload "scalability_traces.tar.gz" -} - echo "--- Clone kibana-load-testing repo and compile project" +mkdir -p "${KIBANA_LOAD_TESTING_DIR}" && cd "${KIBANA_LOAD_TESTING_DIR}" checkout_and_compile_load_runner cd "$KIBANA_DIR" echo "--- Download the latest artifacts from single user performance pipeline" download_artifacts -if [ "$BUILDKITE_PIPELINE_SLUG" == "kibana-scalability-benchmarking-1" ]; then - echo "--- Run journey scalability tests" - node scripts/run_scalability --kibana-install-dir "$KIBANA_BUILD_LOCATION" --journey-path "scalability_traces/server" -else - echo "--- Run single apis capacity tests" - node scripts/run_scalability --kibana-install-dir "$KIBANA_BUILD_LOCATION" --journey-path "x-pack/test/scalability/apis" -fi +echo "--- Run journey scalability tests" +node scripts/run_scalability --kibana-install-dir "$KIBANA_BUILD_LOCATION" --journey-path "scalability_traces/server" echo "--- Upload test results" upload_test_results + +cd "${LATEST_RUN_ARTIFACTS_DIR}" +echo "Upload scalability traces as build artifacts" +buildkite-agent artifact upload "scalability_traces.tar.gz" diff --git a/.buildkite/scripts/steps/scalability/util.sh b/.buildkite/scripts/steps/scalability/util.sh new file mode 100755 index 0000000000000..8e1f026feecb1 --- /dev/null +++ b/.buildkite/scripts/steps/scalability/util.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash + +checkout_and_compile_load_runner() { + if [[ ! -d .git ]]; then + git init + git remote add origin https://github.com/elastic/kibana-load-testing.git + fi + git fetch origin --depth 1 "main" + git reset --hard FETCH_HEAD + + KIBANA_LOAD_TESTING_GIT_COMMIT="$(git rev-parse HEAD)" + export KIBANA_LOAD_TESTING_GIT_COMMIT + + mvn -q test-compile + echo "Set 'GATLING_PROJECT_PATH' env var for ScalabilityTestRunner" + export GATLING_PROJECT_PATH="$(pwd)" +} + +upload_test_results() { + echo "Upload server logs as build artifacts" + tar -czf server-logs.tar.gz data/ftr_servers_logs/**/* + buildkite-agent artifact upload server-logs.tar.gz + echo "--- Upload Gatling reports as build artifacts" + tar -czf "scalability_test_report.tar.gz" --exclude=simulation.log -C kibana-load-testing/target gatling + buildkite-agent artifact upload "scalability_test_report.tar.gz" +} + +bootstrap_kibana() { + echo "--- yarn kbn bootstrap --force-install" + if ! yarn kbn bootstrap --force-install; then + echo "bootstrap failed, trying again in 15 seconds" + sleep 15 + + rm -rf node_modules + + echo "--- yarn kbn reset && yarn kbn bootstrap, attempt 2" + yarn kbn reset && yarn kbn bootstrap + fi +} diff --git a/.buildkite/scripts/steps/storybooks/build_and_upload.ts b/.buildkite/scripts/steps/storybooks/build_and_upload.ts index fbe8daee88370..949cb0a0ff534 100644 --- a/.buildkite/scripts/steps/storybooks/build_and_upload.ts +++ b/.buildkite/scripts/steps/storybooks/build_and_upload.ts @@ -19,7 +19,7 @@ const STORYBOOKS = [ 'cloud_chat', 'coloring', 'chart_icons', - 'content_management_plugin', + 'content_management_examples', 'controls', 'custom_integrations', 'dashboard_enhanced', diff --git a/.eslintrc.js b/.eslintrc.js index 221453e382802..f32b6498d9981 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -778,6 +778,11 @@ module.exports = { name: 'lodash/fp/assocPath', message: 'Please use @kbn/safer-lodash-set/fp/assocPath instead', }, + { + name: 'lodash/fp/template', + message: + 'lodash.template is unsafe, and not compatible with our content security policy.', + }, { name: 'lodash/template', message: diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 552699731ce67..27a7f01ec78e9 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -19,6 +19,7 @@ x-pack/examples/alerting_example @elastic/response-ops x-pack/test/functional_with_es_ssl/plugins/alerts @elastic/response-ops x-pack/plugins/alerting @elastic/response-ops packages/kbn-alerts @elastic/security-solution +packages/kbn-alerts-as-data-utils @elastic/response-ops x-pack/test/alerting_api_integration/common/plugins/alerts_restricted @elastic/response-ops packages/kbn-alerts-ui-shared @elastic/response-ops packages/kbn-ambient-common-types @elastic/kibana-operations @@ -82,6 +83,7 @@ packages/kbn-config-mocks @elastic/kibana-core packages/kbn-config-schema @elastic/kibana-core src/plugins/console @elastic/platform-deployment-management packages/content-management/content_editor @elastic/appex-sharedux +examples/content_management_examples @elastic/appex-sharedux src/plugins/content_management @elastic/appex-sharedux packages/content-management/table_list @elastic/appex-sharedux examples/controls_example @elastic/kibana-presentation @@ -336,6 +338,7 @@ x-pack/test/encrypted_saved_objects_api_integration/plugins/api_consumer_plugin src/plugins/event_annotation @elastic/kibana-visualizations x-pack/test/plugin_api_integration/plugins/event_log @elastic/response-ops x-pack/plugins/event_log @elastic/response-ops +packages/kbn-expandable-flyout @elastic/security-threat-hunting-investigations packages/kbn-expect @elastic/kibana-operations x-pack/examples/exploratory_view_example @elastic/uptime src/plugins/expression_error @elastic/kibana-presentation @@ -760,9 +763,9 @@ packages/kbn-yarn-lock-validator @elastic/kibana-operations # /x-pack/test/observability_functional @elastic/unified-observability # Home/Overview/Landing Pages -/x-pack/plugins/observability/public/pages/home @elastic/observability-design @elastic/observability-ui -/x-pack/plugins/observability/public/pages/landing @elastic/observability-design @elastic/observability-ui -/x-pack/plugins/observability/public/pages/overview @elastic/observability-design @elastic/observability-ui +/x-pack/plugins/observability/public/pages/home @elastic/observability-ui +/x-pack/plugins/observability/public/pages/landing @elastic/observability-ui +/x-pack/plugins/observability/public/pages/overview @elastic/observability-ui # Actionable Observability /x-pack/test/observability_functional @elastic/actionable-observability @@ -908,6 +911,7 @@ packages/kbn-yarn-lock-validator @elastic/kibana-operations # Kibana Platform Security /.github/codeql @elastic/kibana-security /.github/workflows/codeql.yml @elastic/kibana-security +/src/dev/eslint/security_eslint_rule_tests.ts @elastic/kibana-security /src/plugins/telemetry/server/config/telemetry_labels.ts @elastic/kibana-security /test/interactive_setup_api_integration/ @elastic/kibana-security /test/interactive_setup_functional/ @elastic/kibana-security @@ -1040,7 +1044,6 @@ packages/kbn-yarn-lock-validator @elastic/kibana-operations /x-pack/plugins/security_solution/server/lib/detection_engine/migrations @elastic/security-detections-response-alerts /x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview @elastic/security-detections-response-alerts /x-pack/plugins/security_solution/server/lib/detection_engine/rule_types @elastic/security-detections-response-alerts -/x-pack/plugins/security_solution/server/lib/detection_engine/signals @elastic/security-detections-response-alerts /x-pack/plugins/security_solution/server/lib/detection_engine/routes/index @elastic/security-detections-response-alerts /x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals @elastic/security-detections-response-alerts diff --git a/.github/workflows/skip-failed-test.yml b/.github/workflows/skip-failed-test.yml index e892582951adc..a6535b106c728 100644 --- a/.github/workflows/skip-failed-test.yml +++ b/.github/workflows/skip-failed-test.yml @@ -26,6 +26,7 @@ jobs: uses: ./actions/permission-check with: permission: admin + teams: appex-qa token: ${{secrets.GITHUB_TOKEN}} - name: Checkout kibana-operations diff --git a/.i18nrc.json b/.i18nrc.json index d8a6b4689f78e..b7d7432551faa 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -25,6 +25,7 @@ "embeddableExamples": "examples/embeddable_examples", "esQuery": "packages/kbn-es-query/src", "esUi": "src/plugins/es_ui_shared", + "expandableFlyout": "packages/kbn-expandable-flyout", "expressionError": "src/plugins/expression_error", "expressionGauge": "src/plugins/chart_expressions/expression_gauge", "expressionHeatmap": "src/plugins/chart_expressions/expression_heatmap", diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index 80f6b4de55be0..0b8e633709e11 100644 --- a/api_docs/actions.mdx +++ b/api_docs/actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/actions title: "actions" image: https://source.unsplash.com/400x175/?github description: API docs for the actions plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'actions'] --- import actionsObj from './actions.devdocs.json'; diff --git a/api_docs/advanced_settings.mdx b/api_docs/advanced_settings.mdx index e483bc4e6c1f6..923c752e5b8ee 100644 --- a/api_docs/advanced_settings.mdx +++ b/api_docs/advanced_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/advancedSettings title: "advancedSettings" image: https://source.unsplash.com/400x175/?github description: API docs for the advancedSettings plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] --- import advancedSettingsObj from './advanced_settings.devdocs.json'; diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index f642582240e5f..7570552b45d10 100644 --- a/api_docs/aiops.mdx +++ b/api_docs/aiops.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiops title: "aiops" image: https://source.unsplash.com/400x175/?github description: API docs for the aiops plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiops'] --- import aiopsObj from './aiops.devdocs.json'; diff --git a/api_docs/alerting.devdocs.json b/api_docs/alerting.devdocs.json index 0a9c5b5718503..016e965013dfe 100644 --- a/api_docs/alerting.devdocs.json +++ b/api_docs/alerting.devdocs.json @@ -867,6 +867,69 @@ } ], "functions": [ + { + "parentPluginId": "alerting", + "id": "def-server.getComponentTemplate", + "type": "Function", + "tags": [], + "label": "getComponentTemplate", + "description": [], + "signature": [ + "(fieldMap: ", + { + "pluginId": "@kbn/alerts-as-data-utils", + "scope": "common", + "docId": "kibKbnAlertsAsDataUtilsPluginApi", + "section": "def-common.FieldMap", + "text": "FieldMap" + }, + ", context?: string | undefined) => ", + "ClusterPutComponentTemplateRequest" + ], + "path": "x-pack/plugins/alerting/server/alerts_service/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "alerting", + "id": "def-server.getComponentTemplate.$1", + "type": "Object", + "tags": [], + "label": "fieldMap", + "description": [], + "signature": [ + { + "pluginId": "@kbn/alerts-as-data-utils", + "scope": "common", + "docId": "kibKbnAlertsAsDataUtilsPluginApi", + "section": "def-common.FieldMap", + "text": "FieldMap" + } + ], + "path": "x-pack/plugins/alerting/server/alerts_service/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "alerting", + "id": "def-server.getComponentTemplate.$2", + "type": "string", + "tags": [], + "label": "context", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/alerting/server/alerts_service/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "alerting", "id": "def-server.getEsErrorMessage", @@ -3239,6 +3302,33 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "alerting", + "id": "def-server.ECS_COMPONENT_TEMPLATE_NAME", + "type": "string", + "tags": [], + "label": "ECS_COMPONENT_TEMPLATE_NAME", + "description": [], + "path": "x-pack/plugins/alerting/server/alerts_service/alerts_service.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "alerting", + "id": "def-server.ECS_CONTEXT", + "type": "string", + "tags": [], + "label": "ECS_CONTEXT", + "description": [], + "signature": [ + "\"ecs\"" + ], + "path": "x-pack/plugins/alerting/server/alerts_service/alerts_service.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "alerting", "id": "def-server.ExecutorType", @@ -3938,6 +4028,42 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "alerting", + "id": "def-common.getComponentTemplateFromFieldMap", + "type": "Function", + "tags": [], + "label": "getComponentTemplateFromFieldMap", + "description": [], + "signature": [ + "({ name, fieldMap, fieldLimit, }: ", + "GetComponentTemplateFromFieldMapOpts", + ") => ", + "ClusterPutComponentTemplateRequest" + ], + "path": "x-pack/plugins/alerting/common/alert_schema/field_maps/component_template_from_field_map.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "alerting", + "id": "def-common.getComponentTemplateFromFieldMap.$1", + "type": "Object", + "tags": [], + "label": "{\n name,\n fieldMap,\n fieldLimit,\n}", + "description": [], + "signature": [ + "GetComponentTemplateFromFieldMapOpts" + ], + "path": "x-pack/plugins/alerting/common/alert_schema/field_maps/component_template_from_field_map.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "alerting", "id": "def-common.getDurationNumberInItsUnit", @@ -4052,6 +4178,69 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "alerting", + "id": "def-common.mappingFromFieldMap", + "type": "Function", + "tags": [], + "label": "mappingFromFieldMap", + "description": [], + "signature": [ + "(fieldMap: ", + { + "pluginId": "@kbn/alerts-as-data-utils", + "scope": "common", + "docId": "kibKbnAlertsAsDataUtilsPluginApi", + "section": "def-common.FieldMap", + "text": "FieldMap" + }, + ", dynamic: boolean | \"strict\") => ", + "MappingTypeMapping" + ], + "path": "x-pack/plugins/alerting/common/alert_schema/field_maps/mapping_from_field_map.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "alerting", + "id": "def-common.mappingFromFieldMap.$1", + "type": "Object", + "tags": [], + "label": "fieldMap", + "description": [], + "signature": [ + { + "pluginId": "@kbn/alerts-as-data-utils", + "scope": "common", + "docId": "kibKbnAlertsAsDataUtilsPluginApi", + "section": "def-common.FieldMap", + "text": "FieldMap" + } + ], + "path": "x-pack/plugins/alerting/common/alert_schema/field_maps/mapping_from_field_map.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "alerting", + "id": "def-common.mappingFromFieldMap.$2", + "type": "CompoundType", + "tags": [], + "label": "dynamic", + "description": [], + "signature": [ + "boolean | \"strict\"" + ], + "path": "x-pack/plugins/alerting/common/alert_schema/field_maps/mapping_from_field_map.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "alerting", "id": "def-common.parseDuration", diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx index bb1d5f0a4933a..896d6335b1e48 100644 --- a/api_docs/alerting.mdx +++ b/api_docs/alerting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/alerting title: "alerting" image: https://source.unsplash.com/400x175/?github description: API docs for the alerting plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'alerting'] --- import alertingObj from './alerting.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-o | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 487 | 1 | 476 | 40 | +| 497 | 1 | 486 | 41 | ## Client diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index 90778909b2d8b..064ab9c52e28f 100644 --- a/api_docs/apm.mdx +++ b/api_docs/apm.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apm title: "apm" image: https://source.unsplash.com/400x175/?github description: API docs for the apm plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index 22dd180a466d3..0d3965826809e 100644 --- a/api_docs/banners.mdx +++ b/api_docs/banners.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/banners title: "banners" image: https://source.unsplash.com/400x175/?github description: API docs for the banners plugin -date: 2023-02-23 +date: 2023-02-28 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 22ea0d1b07d2c..e742fdf8dfc18 100644 --- a/api_docs/bfetch.mdx +++ b/api_docs/bfetch.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/bfetch title: "bfetch" image: https://source.unsplash.com/400x175/?github description: API docs for the bfetch plugin -date: 2023-02-23 +date: 2023-02-28 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 5e5ff4954e1e1..32b1952cafdd8 100644 --- a/api_docs/canvas.mdx +++ b/api_docs/canvas.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/canvas title: "canvas" image: https://source.unsplash.com/400x175/?github description: API docs for the canvas plugin -date: 2023-02-23 +date: 2023-02-28 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 2cd97e218cc14..1ed8c149e896c 100644 --- a/api_docs/cases.mdx +++ b/api_docs/cases.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cases title: "cases" image: https://source.unsplash.com/400x175/?github description: API docs for the cases plugin -date: 2023-02-23 +date: 2023-02-28 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 0b0e945036c16..9d36327064aec 100644 --- a/api_docs/charts.mdx +++ b/api_docs/charts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/charts title: "charts" image: https://source.unsplash.com/400x175/?github description: API docs for the charts plugin -date: 2023-02-23 +date: 2023-02-28 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 92f3a4db2022b..659b85d1d7b28 100644 --- a/api_docs/cloud.mdx +++ b/api_docs/cloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloud title: "cloud" image: https://source.unsplash.com/400x175/?github description: API docs for the cloud plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; diff --git a/api_docs/cloud_chat.mdx b/api_docs/cloud_chat.mdx index 75a5783bf1c4d..9f42c75ffb3e9 100644 --- a/api_docs/cloud_chat.mdx +++ b/api_docs/cloud_chat.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudChat title: "cloudChat" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudChat plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudChat'] --- import cloudChatObj from './cloud_chat.devdocs.json'; diff --git a/api_docs/cloud_data_migration.mdx b/api_docs/cloud_data_migration.mdx index b53dc41411420..3809b2b6a701f 100644 --- a/api_docs/cloud_data_migration.mdx +++ b/api_docs/cloud_data_migration.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDataMigration title: "cloudDataMigration" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDataMigration plugin -date: 2023-02-23 +date: 2023-02-28 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 f59ffe4b94363..da868144c7618 100644 --- a/api_docs/cloud_defend.mdx +++ b/api_docs/cloud_defend.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDefend title: "cloudDefend" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDefend plugin -date: 2023-02-23 +date: 2023-02-28 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 534d52b7de62a..4bb90591915e0 100644 --- a/api_docs/cloud_experiments.mdx +++ b/api_docs/cloud_experiments.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudExperiments title: "cloudExperiments" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudExperiments plugin -date: 2023-02-23 +date: 2023-02-28 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 0022f8c7a41f1..284069b14370d 100644 --- a/api_docs/cloud_security_posture.mdx +++ b/api_docs/cloud_security_posture.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudSecurityPosture title: "cloudSecurityPosture" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudSecurityPosture plugin -date: 2023-02-23 +date: 2023-02-28 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 2bd917f675f68..e8e668d7e0993 100644 --- a/api_docs/console.mdx +++ b/api_docs/console.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/console title: "console" image: https://source.unsplash.com/400x175/?github description: API docs for the console plugin -date: 2023-02-23 +date: 2023-02-28 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 d358b3b6aba40..7b367b913e7e1 100644 --- a/api_docs/content_management.mdx +++ b/api_docs/content_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/contentManagement title: "contentManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the contentManagement plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'contentManagement'] --- import contentManagementObj from './content_management.devdocs.json'; diff --git a/api_docs/controls.devdocs.json b/api_docs/controls.devdocs.json index fa83ba64ab654..16932ee849930 100644 --- a/api_docs/controls.devdocs.json +++ b/api_docs/controls.devdocs.json @@ -699,6 +699,22 @@ "deprecated": false, "trackAdoption": false, "isRequired": false + }, + { + "parentPluginId": "controls", + "id": "def-public.ControlGroupContainer.Unnamed.$4", + "type": "Object", + "tags": [], + "label": "settings", + "description": [], + "signature": [ + "ControlGroupSettings", + " | undefined" + ], + "path": "src/plugins/controls/public/control_group/embeddable/control_group_container.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": false } ], "returnComment": [] @@ -1412,7 +1428,9 @@ "section": "def-public.ContainerOutput", "text": "ContainerOutput" }, - "> | undefined) => Promise<", + "> | undefined, settings?: ", + "ControlGroupSettings", + " | undefined) => Promise<", { "pluginId": "controls", "scope": "public", @@ -1484,6 +1502,22 @@ "deprecated": false, "trackAdoption": false, "isRequired": false + }, + { + "parentPluginId": "controls", + "id": "def-public.ControlGroupContainerFactory.create.$3", + "type": "Object", + "tags": [], + "label": "settings", + "description": [], + "signature": [ + "ControlGroupSettings", + " | undefined" + ], + "path": "src/plugins/controls/public/control_group/embeddable/control_group_container_factory.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false } ], "returnComment": [] @@ -3315,7 +3349,7 @@ "section": "def-public.ControlGroupRendererProps", "text": "ControlGroupRendererProps" }, - "> & { readonly _result: ({ onLoadComplete, getInitialInput, filters, timeRange, query, }: ", + "> & { readonly _result: ({ onLoadComplete, getCreationOptions, filters, timeRange, query, }: ", { "pluginId": "controls", "scope": "public", @@ -3402,30 +3436,14 @@ "<", "ControlGroupReduxState", ">, action: { payload: { ids: string[]; }; type: string; }) => void; }, ", - { - "pluginId": "embeddable", - "scope": "public", - "docId": "kibEmbeddablePluginApi", - "section": "def-public.Embeddable", - "text": "Embeddable" - }, - "<", - { - "pluginId": "controls", - "scope": "common", - "docId": "kibControlsPluginApi", - "section": "def-common.ControlGroupInput", - "text": "ControlGroupInput" - }, - ", ", { "pluginId": "controls", "scope": "public", "docId": "kibControlsPluginApi", - "section": "def-public.ControlGroupOutput", - "text": "ControlGroupOutput" + "section": "def-public.ControlGroupContainer", + "text": "ControlGroupContainer" }, - ", any>>" + ">" ], "path": "src/plugins/controls/public/control_group/control_group_renderer.tsx", "deprecated": false, @@ -3829,10 +3847,10 @@ }, { "parentPluginId": "controls", - "id": "def-public.ControlGroupRendererProps.getInitialInput", + "id": "def-public.ControlGroupRendererProps.getCreationOptions", "type": "Function", "tags": [], - "label": "getInitialInput", + "label": "getCreationOptions", "description": [], "signature": [ "(initialInput: Partial<", @@ -3893,15 +3911,9 @@ "section": "def-common.ControlGroupInput", "text": "ControlGroupInput" }, - ">) => void; }) => Promise>" + ">) => void; }) => Promise<", + "ControlGroupCreationOptions", + ">" ], "path": "src/plugins/controls/public/control_group/control_group_renderer.tsx", "deprecated": false, @@ -3909,7 +3921,7 @@ "children": [ { "parentPluginId": "controls", - "id": "def-public.ControlGroupRendererProps.getInitialInput.$1", + "id": "def-public.ControlGroupRendererProps.getCreationOptions.$1", "type": "Object", "tags": [], "label": "initialInput", @@ -3932,7 +3944,7 @@ }, { "parentPluginId": "controls", - "id": "def-public.ControlGroupRendererProps.getInitialInput.$2", + "id": "def-public.ControlGroupRendererProps.getCreationOptions.$2", "type": "Object", "tags": [], "label": "builder", diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index e53ff3cac37e3..92cff1862b49c 100644 --- a/api_docs/controls.mdx +++ b/api_docs/controls.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/controls title: "controls" image: https://source.unsplash.com/400x175/?github description: API docs for the controls plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'controls'] --- import controlsObj from './controls.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kib | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 270 | 0 | 266 | 9 | +| 272 | 0 | 268 | 11 | ## Client diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index 82986b0aae745..00fd8abf5d551 100644 --- a/api_docs/custom_integrations.mdx +++ b/api_docs/custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/customIntegrations title: "customIntegrations" image: https://source.unsplash.com/400x175/?github description: API docs for the customIntegrations plugin -date: 2023-02-23 +date: 2023-02-28 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 3e9109a796427..6f7f587f59345 100644 --- a/api_docs/dashboard.mdx +++ b/api_docs/dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboard title: "dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboard plugin -date: 2023-02-23 +date: 2023-02-28 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 fcd06015fce0b..2c0a9a906d5d2 100644 --- a/api_docs/dashboard_enhanced.mdx +++ b/api_docs/dashboard_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboardEnhanced title: "dashboardEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboardEnhanced plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboardEnhanced'] --- import dashboardEnhancedObj from './dashboard_enhanced.devdocs.json'; diff --git a/api_docs/data.devdocs.json b/api_docs/data.devdocs.json index f7ba2e567ec43..b2be9a6510899 100644 --- a/api_docs/data.devdocs.json +++ b/api_docs/data.devdocs.json @@ -10729,14 +10729,6 @@ "plugin": "dataViews", "path": "src/plugins/data_views/server/utils.ts" }, - { - "plugin": "discover", - "path": "src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts" - }, - { - "plugin": "discover", - "path": "src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts" - }, { "plugin": "dataViews", "path": "src/plugins/data_views/public/saved_objects_client_wrapper.ts" @@ -13567,7 +13559,7 @@ }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_query_filter.ts" + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_query_filter.ts" }, { "plugin": "timelines", @@ -21190,7 +21182,7 @@ }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_query_filter.ts" + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_query_filter.ts" }, { "plugin": "timelines", @@ -28514,14 +28506,6 @@ "plugin": "dataViews", "path": "src/plugins/data_views/server/utils.ts" }, - { - "plugin": "discover", - "path": "src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts" - }, - { - "plugin": "discover", - "path": "src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts" - }, { "plugin": "dataViews", "path": "src/plugins/data_views/public/saved_objects_client_wrapper.ts" diff --git a/api_docs/data.mdx b/api_docs/data.mdx index 7f755296c95b4..1e4c203e60816 100644 --- a/api_docs/data.mdx +++ b/api_docs/data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data title: "data" image: https://source.unsplash.com/400x175/?github description: API docs for the data plugin -date: 2023-02-23 +date: 2023-02-28 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 aade3428f442b..5480e56f276a5 100644 --- a/api_docs/data_query.mdx +++ b/api_docs/data_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-query title: "data.query" image: https://source.unsplash.com/400x175/?github description: API docs for the data.query plugin -date: 2023-02-23 +date: 2023-02-28 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 1acee53c5b4ef..c2771042aa461 100644 --- a/api_docs/data_search.mdx +++ b/api_docs/data_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-search title: "data.search" image: https://source.unsplash.com/400x175/?github description: API docs for the data.search plugin -date: 2023-02-23 +date: 2023-02-28 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 18cef37ba6311..75735009f549a 100644 --- a/api_docs/data_view_editor.mdx +++ b/api_docs/data_view_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewEditor title: "dataViewEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewEditor plugin -date: 2023-02-23 +date: 2023-02-28 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 92d5c7903ab7a..f1e63364df52b 100644 --- a/api_docs/data_view_field_editor.mdx +++ b/api_docs/data_view_field_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewFieldEditor title: "dataViewFieldEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewFieldEditor plugin -date: 2023-02-23 +date: 2023-02-28 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 2562327812ffc..991044de8198b 100644 --- a/api_docs/data_view_management.mdx +++ b/api_docs/data_view_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewManagement title: "dataViewManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewManagement plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewManagement'] --- import dataViewManagementObj from './data_view_management.devdocs.json'; diff --git a/api_docs/data_views.devdocs.json b/api_docs/data_views.devdocs.json index 2ca57d5769f28..ba7f5aa586f15 100644 --- a/api_docs/data_views.devdocs.json +++ b/api_docs/data_views.devdocs.json @@ -97,7 +97,7 @@ }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_query_filter.ts" + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_query_filter.ts" }, { "plugin": "timelines", @@ -8305,7 +8305,7 @@ }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_query_filter.ts" + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_query_filter.ts" }, { "plugin": "timelines", @@ -15608,7 +15608,7 @@ }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_query_filter.ts" + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_query_filter.ts" }, { "plugin": "timelines", @@ -25935,14 +25935,6 @@ "plugin": "data", "path": "src/plugins/data/server/query/route_handler_context.test.ts" }, - { - "plugin": "discover", - "path": "src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts" - }, - { - "plugin": "discover", - "path": "src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts" - }, { "plugin": "data", "path": "src/plugins/data/public/search/session/sessions_mgmt/lib/api.ts" diff --git a/api_docs/data_views.mdx b/api_docs/data_views.mdx index 12327bd0a7233..9d6efbcd459f6 100644 --- a/api_docs/data_views.mdx +++ b/api_docs/data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViews title: "dataViews" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViews plugin -date: 2023-02-23 +date: 2023-02-28 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 5441d276a8d59..4f8549bfe3799 100644 --- a/api_docs/data_visualizer.mdx +++ b/api_docs/data_visualizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataVisualizer title: "dataVisualizer" image: https://source.unsplash.com/400x175/?github description: API docs for the dataVisualizer plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer'] --- import dataVisualizerObj from './data_visualizer.devdocs.json'; diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index 704a23cce5f6c..a4f0482e3da88 100644 --- a/api_docs/deprecations_by_api.mdx +++ b/api_docs/deprecations_by_api.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByApi slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-api title: Deprecated API usage by API description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -38,11 +38,11 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | alerting, discover, securitySolution | - | | | actions, alerting | - | | | @kbn/core-saved-objects-migration-server-internal, actions, dataViews, data, alerting, savedObjectsTagging, savedSearch, canvas, lens, cases, graph, lists, maps, securitySolution, dashboard, visualizations, @kbn/core-test-helpers-so-type-serializer | - | -| | @kbn/core-saved-objects-common, @kbn/core-saved-objects-api-browser, @kbn/core-saved-objects-browser-internal, @kbn/core-saved-objects-api-server, @kbn/core, home, dataViews, discover, savedObjectsTagging, fleet, canvas, osquery, securitySolution, synthetics, savedObjects, @kbn/core-saved-objects-browser-mocks, @kbn/core-saved-objects-import-export-server-internal, apm, savedObjectsTaggingOss, cases, lists, upgradeAssistant, savedObjectsManagement, @kbn/core-ui-settings-server-internal, dashboard | - | -| | @kbn/core-saved-objects-common, @kbn/core-saved-objects-api-browser, @kbn/core-saved-objects-browser-internal, @kbn/core-saved-objects-api-server, @kbn/core, home, dataViews, discover, savedObjectsTagging, fleet, canvas, osquery, securitySolution, synthetics, savedObjects, @kbn/core-saved-objects-browser-mocks, @kbn/core-saved-objects-import-export-server-internal, apm, savedObjectsTaggingOss, cases, lists, upgradeAssistant, savedObjectsManagement, @kbn/core-ui-settings-server-internal, data | - | | | discover | - | | | data, discover, imageEmbeddable, embeddable | - | | | advancedSettings, discover | - | +| | @kbn/core-saved-objects-common, @kbn/core-saved-objects-api-browser, @kbn/core-saved-objects-browser-internal, @kbn/core-saved-objects-api-server, @kbn/core, home, dataViews, savedObjectsTagging, fleet, canvas, osquery, securitySolution, synthetics, savedObjects, @kbn/core-saved-objects-browser-mocks, @kbn/core-saved-objects-import-export-server-internal, apm, savedObjectsTaggingOss, cases, lists, upgradeAssistant, savedObjectsManagement, @kbn/core-ui-settings-server-internal, dashboard | - | +| | @kbn/core-saved-objects-common, @kbn/core-saved-objects-api-browser, @kbn/core-saved-objects-browser-internal, @kbn/core-saved-objects-api-server, @kbn/core, home, dataViews, savedObjectsTagging, fleet, canvas, osquery, securitySolution, synthetics, savedObjects, @kbn/core-saved-objects-browser-mocks, @kbn/core-saved-objects-import-export-server-internal, apm, savedObjectsTaggingOss, cases, lists, upgradeAssistant, savedObjectsManagement, @kbn/core-ui-settings-server-internal, data | - | | | securitySolution | - | | | lists, securitySolution, @kbn/securitysolution-io-ts-list-types | - | | | lists, securitySolution, @kbn/securitysolution-io-ts-list-types | - | diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 5f7f7cef6b6df..6a179d1e6e930 100644 --- a/api_docs/deprecations_by_plugin.mdx +++ b/api_docs/deprecations_by_plugin.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByPlugin slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-plugin title: Deprecated API usage by plugin description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -564,8 +564,6 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| | | [saved_search_embeddable.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx#:~:text=create), [saved_search_embeddable.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx#:~:text=create) | - | -| | [get_layout_props.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts#:~:text=SavedObject), [get_layout_props.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts#:~:text=SavedObject) | - | -| | [get_layout_props.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts#:~:text=SavedObject), [get_layout_props.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts#:~:text=SavedObject), [get_layout_props.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts#:~:text=SavedObject), [get_layout_props.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts#:~:text=SavedObject), [get_layout_props.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts#:~:text=SavedObject), [get_layout_props.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts#:~:text=SavedObject) | - | | | [use_text_based_query_language.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/hooks/use_text_based_query_language.ts#:~:text=title), [use_text_based_query_language.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/main/hooks/use_text_based_query_language.ts#:~:text=title) | - | | | [saved_search_embeddable.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx#:~:text=create), [saved_search_embeddable.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx#:~:text=create) | - | | | [fetch_hits_in_interval.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/context/utils/fetch_hits_in_interval.ts#:~:text=EsQuerySearchAfter), [fetch_hits_in_interval.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/context/utils/fetch_hits_in_interval.ts#:~:text=EsQuerySearchAfter), [get_es_query_search_after.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/context/utils/get_es_query_search_after.ts#:~:text=EsQuerySearchAfter), [get_es_query_search_after.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/context/utils/get_es_query_search_after.ts#:~:text=EsQuerySearchAfter), [get_es_query_search_after.ts](https://github.com/elastic/kibana/tree/main/src/plugins/discover/public/application/context/utils/get_es_query_search_after.ts#:~:text=EsQuerySearchAfter) | - | @@ -758,7 +756,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [use_create_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_create_saved_object.tsx#:~:text=SavedObjectsCreateOptions), [use_create_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_create_saved_object.tsx#:~:text=SavedObjectsCreateOptions), [use_update_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_update_saved_object.tsx#:~:text=SavedObjectsCreateOptions), [use_update_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_update_saved_object.tsx#:~:text=SavedObjectsCreateOptions) | - | | | [use_find_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_find_saved_object.tsx#:~:text=SavedObjectsBatchResponse), [use_find_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_find_saved_object.tsx#:~:text=SavedObjectsBatchResponse) | - | | | [use_find_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_find_saved_object.tsx#:~:text=SavedObjectAttributes), [use_find_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_find_saved_object.tsx#:~:text=SavedObjectAttributes), [use_create_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_create_saved_object.tsx#:~:text=SavedObjectAttributes), [use_create_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_create_saved_object.tsx#:~:text=SavedObjectAttributes), [use_create_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_create_saved_object.tsx#:~:text=SavedObjectAttributes), [use_get_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_get_saved_object.tsx#:~:text=SavedObjectAttributes), [use_get_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_get_saved_object.tsx#:~:text=SavedObjectAttributes), [use_update_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_update_saved_object.tsx#:~:text=SavedObjectAttributes), [use_update_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_update_saved_object.tsx#:~:text=SavedObjectAttributes), [use_update_saved_object.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/hooks/use_update_saved_object.tsx#:~:text=SavedObjectAttributes)+ 2 more | - | -| | [log_threshold_references_manager.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_references_manager.ts#:~:text=SavedObjectReference), [log_threshold_references_manager.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_references_manager.ts#:~:text=SavedObjectReference), [log_threshold_references_manager.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_references_manager.ts#:~:text=SavedObjectReference), [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/types.ts#:~:text=SavedObjectReference), [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/types.ts#:~:text=SavedObjectReference), [cpu.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/cpu.ts#:~:text=SavedObjectReference), [cpu.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/cpu.ts#:~:text=SavedObjectReference), [load.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/load.ts#:~:text=SavedObjectReference), [load.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/load.ts#:~:text=SavedObjectReference), [memory.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/memory.ts#:~:text=SavedObjectReference)+ 11 more | - | +| | [log_threshold_references_manager.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_references_manager.ts#:~:text=SavedObjectReference), [log_threshold_references_manager.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_references_manager.ts#:~:text=SavedObjectReference), [log_threshold_references_manager.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_references_manager.ts#:~:text=SavedObjectReference), [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/types.ts#:~:text=SavedObjectReference), [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/types.ts#:~:text=SavedObjectReference), [cpu.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/cpu.ts#:~:text=SavedObjectReference), [cpu.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/cpu.ts#:~:text=SavedObjectReference), [load.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/load.ts#:~:text=SavedObjectReference), [load.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/load.ts#:~:text=SavedObjectReference), [memory.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/infra/public/common/visualizations/lens/hosts/memory.ts#:~:text=SavedObjectReference)+ 13 more | - | @@ -1104,15 +1102,15 @@ migrates to using the Kibana Privilege model: https://github.com/elastic/kibana/ | | [utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/dashboards/utils.ts#:~:text=SavedObject), [utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/dashboards/utils.ts#:~:text=SavedObject), [use_security_dashboards_table.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/dashboards/use_security_dashboards_table.tsx#:~:text=SavedObject), [use_security_dashboards_table.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/dashboards/use_security_dashboards_table.tsx#:~:text=SavedObject), [host_risk_score_dashboards.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/saved_object/host_risk_score_dashboards.ts#:~:text=SavedObject), [host_risk_score_dashboards.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/saved_object/host_risk_score_dashboards.ts#:~:text=SavedObject), [user_risk_score_dashboards.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/saved_object/user_risk_score_dashboards.ts#:~:text=SavedObject), [user_risk_score_dashboards.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/saved_object/user_risk_score_dashboards.ts#:~:text=SavedObject) | - | | | [dependencies_start_mock.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/mock/endpoint/dependencies_start_mock.ts#:~:text=indexPatterns) | - | | | [utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/dashboards/utils.ts#:~:text=SavedObject), [utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/dashboards/utils.ts#:~:text=SavedObject), [use_security_dashboards_table.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/dashboards/use_security_dashboards_table.tsx#:~:text=SavedObject), [use_security_dashboards_table.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/dashboards/use_security_dashboards_table.tsx#:~:text=SavedObject), [host_risk_score_dashboards.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/saved_object/host_risk_score_dashboards.ts#:~:text=SavedObject), [host_risk_score_dashboards.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/saved_object/host_risk_score_dashboards.ts#:~:text=SavedObject), [user_risk_score_dashboards.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/saved_object/user_risk_score_dashboards.ts#:~:text=SavedObject), [user_risk_score_dashboards.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/saved_object/user_risk_score_dashboards.ts#:~:text=SavedObject), [utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/dashboards/utils.ts#:~:text=SavedObject), [utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/dashboards/utils.ts#:~:text=SavedObject)+ 14 more | - | -| | [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/source/index.tsx#:~:text=title), [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/source/index.tsx#:~:text=title), [use_rule_from_timeline.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_from_timeline.tsx#:~:text=title), [get_es_query_filter.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/detections/containers/detection_engine/exceptions/get_es_query_filter.ts#:~:text=title), [grouped_alerts.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouped_alerts.tsx#:~:text=title), [utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/utils.ts#:~:text=title), [middleware.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts#:~:text=title), [get_query_filter.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_query_filter.ts#:~:text=title), [index_pattern.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/mock/index_pattern.ts#:~:text=title), [utils.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/utils.test.ts#:~:text=title)+ 20 more | - | +| | [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/source/index.tsx#:~:text=title), [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/source/index.tsx#:~:text=title), [use_rule_from_timeline.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_from_timeline.tsx#:~:text=title), [get_es_query_filter.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/detections/containers/detection_engine/exceptions/get_es_query_filter.ts#:~:text=title), [grouped_alerts.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouped_alerts.tsx#:~:text=title), [utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/utils.ts#:~:text=title), [middleware.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts#:~:text=title), [get_query_filter.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_query_filter.ts#:~:text=title), [index_pattern.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/mock/index_pattern.ts#:~:text=title), [utils.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/utils.test.ts#:~:text=title)+ 20 more | - | | | [wrap_search_source_client.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview/api/preview_rules/wrap_search_source_client.ts#:~:text=create) | - | | | [wrap_search_source_client.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview/api/preview_rules/wrap_search_source_client.test.ts#:~:text=fetch), [wrap_search_source_client.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview/api/preview_rules/wrap_search_source_client.test.ts#:~:text=fetch), [wrap_search_source_client.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview/api/preview_rules/wrap_search_source_client.test.ts#:~:text=fetch), [wrap_search_source_client.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview/api/preview_rules/wrap_search_source_client.test.ts#:~:text=fetch) | - | | | [api.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/hooks/eql/api.ts#:~:text=options) | - | -| | [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/source/index.tsx#:~:text=title), [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/source/index.tsx#:~:text=title), [use_rule_from_timeline.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_from_timeline.tsx#:~:text=title), [get_es_query_filter.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/detections/containers/detection_engine/exceptions/get_es_query_filter.ts#:~:text=title), [grouped_alerts.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouped_alerts.tsx#:~:text=title), [utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/utils.ts#:~:text=title), [middleware.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts#:~:text=title), [get_query_filter.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_query_filter.ts#:~:text=title), [index_pattern.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/mock/index_pattern.ts#:~:text=title), [utils.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/utils.test.ts#:~:text=title)+ 20 more | - | -| | [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/source/index.tsx#:~:text=title), [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/source/index.tsx#:~:text=title), [use_rule_from_timeline.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_from_timeline.tsx#:~:text=title), [get_es_query_filter.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/detections/containers/detection_engine/exceptions/get_es_query_filter.ts#:~:text=title), [grouped_alerts.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouped_alerts.tsx#:~:text=title), [utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/utils.ts#:~:text=title), [middleware.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts#:~:text=title), [get_query_filter.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_query_filter.ts#:~:text=title), [index_pattern.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/mock/index_pattern.ts#:~:text=title), [utils.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/utils.test.ts#:~:text=title)+ 5 more | - | +| | [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/source/index.tsx#:~:text=title), [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/source/index.tsx#:~:text=title), [use_rule_from_timeline.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_from_timeline.tsx#:~:text=title), [get_es_query_filter.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/detections/containers/detection_engine/exceptions/get_es_query_filter.ts#:~:text=title), [grouped_alerts.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouped_alerts.tsx#:~:text=title), [utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/utils.ts#:~:text=title), [middleware.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts#:~:text=title), [get_query_filter.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_query_filter.ts#:~:text=title), [index_pattern.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/mock/index_pattern.ts#:~:text=title), [utils.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/utils.test.ts#:~:text=title)+ 20 more | - | +| | [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/source/index.tsx#:~:text=title), [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/source/index.tsx#:~:text=title), [use_rule_from_timeline.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_from_timeline.tsx#:~:text=title), [get_es_query_filter.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/detections/containers/detection_engine/exceptions/get_es_query_filter.ts#:~:text=title), [grouped_alerts.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouped_alerts.tsx#:~:text=title), [utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/utils.ts#:~:text=title), [middleware.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts#:~:text=title), [get_query_filter.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_query_filter.ts#:~:text=title), [index_pattern.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/mock/index_pattern.ts#:~:text=title), [utils.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/utils.test.ts#:~:text=title)+ 5 more | - | | | [policy_config.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [policy_config.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [policy_config.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [fleet_integration.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts#:~:text=mode), [fleet_integration.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts#:~:text=mode), [create_default_policy.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/handlers/create_default_policy.test.ts#:~:text=mode), [create_default_policy.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/handlers/create_default_policy.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode)+ 7 more | 8.8.0 | | | [policy_config.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [policy_config.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [policy_config.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [fleet_integration.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts#:~:text=mode), [fleet_integration.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts#:~:text=mode), [create_default_policy.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/handlers/create_default_policy.test.ts#:~:text=mode), [create_default_policy.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/handlers/create_default_policy.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode)+ 7 more | 8.8.0 | -| | [query.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/query.ts#:~:text=license%24) | 8.8.0 | +| | [query.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/query.ts#:~:text=license%24) | 8.8.0 | | | [request_context_factory.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/request_context_factory.ts#:~:text=authc), [route.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview/api/preview_rules/route.ts#:~:text=authc), [create_signals_migration_route.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/create_signals_migration_route.ts#:~:text=authc), [delete_signals_migration_route.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/delete_signals_migration_route.ts#:~:text=authc), [finalize_signals_migration_route.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/finalize_signals_migration_route.ts#:~:text=authc), [open_close_signals_route.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals_route.ts#:~:text=authc), [common.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/timeline/utils/common.ts#:~:text=authc) | - | | | [use_dashboard_button_href.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/hooks/use_dashboard_button_href.ts#:~:text=savedObjects), [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/index.tsx#:~:text=savedObjects), [use_security_dashboards_table.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/dashboards/use_security_dashboards_table.tsx#:~:text=savedObjects), [use_create_security_dashboard_link.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/dashboards/use_create_security_dashboard_link.ts#:~:text=savedObjects) | - | | | [utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/dashboards/utils.ts#:~:text=SavedObjectsClientContract), [utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/dashboards/utils.ts#:~:text=SavedObjectsClientContract), [utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/dashboards/utils.ts#:~:text=SavedObjectsClientContract), [utils.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/dashboards/utils.test.ts#:~:text=SavedObjectsClientContract), [utils.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/dashboards/utils.test.ts#:~:text=SavedObjectsClientContract) | - | diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index e6059623d6c2c..729cc3299b1a5 100644 --- a/api_docs/deprecations_by_team.mdx +++ b/api_docs/deprecations_by_team.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsDueByTeam slug: /kibana-dev-docs/api-meta/deprecations-due-by-team title: Deprecated APIs due to be removed, by team description: Lists the teams that are referencing deprecated APIs with a remove by date. -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -154,4 +154,4 @@ migrates to using the Kibana Privilege model: https://github.com/elastic/kibana/ | --------|-------|-----------|-----------| | securitySolution | | [policy_config.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [policy_config.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [policy_config.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [fleet_integration.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts#:~:text=mode), [fleet_integration.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts#:~:text=mode), [create_default_policy.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/handlers/create_default_policy.test.ts#:~:text=mode), [create_default_policy.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/handlers/create_default_policy.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode)+ 7 more | 8.8.0 | | securitySolution | | [policy_config.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [policy_config.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [policy_config.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [fleet_integration.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts#:~:text=mode), [fleet_integration.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts#:~:text=mode), [create_default_policy.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/handlers/create_default_policy.test.ts#:~:text=mode), [create_default_policy.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/handlers/create_default_policy.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode)+ 7 more | 8.8.0 | -| securitySolution | | [query.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/query.ts#:~:text=license%24) | 8.8.0 | \ No newline at end of file +| securitySolution | | [query.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/query.ts#:~:text=license%24) | 8.8.0 | \ No newline at end of file diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index 673fafe261fd7..c6ba017a7eea2 100644 --- a/api_docs/dev_tools.mdx +++ b/api_docs/dev_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/devTools title: "devTools" image: https://source.unsplash.com/400x175/?github description: API docs for the devTools plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'devTools'] --- import devToolsObj from './dev_tools.devdocs.json'; diff --git a/api_docs/discover.mdx b/api_docs/discover.mdx index 763837df66c2f..928f2d7a7c7b2 100644 --- a/api_docs/discover.mdx +++ b/api_docs/discover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discover title: "discover" image: https://source.unsplash.com/400x175/?github description: API docs for the discover plugin -date: 2023-02-23 +date: 2023-02-28 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 38b363dad797c..e54cece7c8f15 100644 --- a/api_docs/discover_enhanced.mdx +++ b/api_docs/discover_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discoverEnhanced title: "discoverEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the discoverEnhanced plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] --- import discoverEnhancedObj from './discover_enhanced.devdocs.json'; diff --git a/api_docs/ecs_data_quality_dashboard.mdx b/api_docs/ecs_data_quality_dashboard.mdx index 5682ed8b92f08..686c30001f9f2 100644 --- a/api_docs/ecs_data_quality_dashboard.mdx +++ b/api_docs/ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ecsDataQualityDashboard title: "ecsDataQualityDashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the ecsDataQualityDashboard plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ecsDataQualityDashboard'] --- import ecsDataQualityDashboardObj from './ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index 72491b8d6002e..e073454315134 100644 --- a/api_docs/embeddable.mdx +++ b/api_docs/embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddable title: "embeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddable plugin -date: 2023-02-23 +date: 2023-02-28 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 b51c1609ecd51..6932c93c6692c 100644 --- a/api_docs/embeddable_enhanced.mdx +++ b/api_docs/embeddable_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddableEnhanced title: "embeddableEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddableEnhanced plugin -date: 2023-02-23 +date: 2023-02-28 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 227c8f4f88c77..eb2d18465b28d 100644 --- a/api_docs/encrypted_saved_objects.mdx +++ b/api_docs/encrypted_saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/encryptedSavedObjects title: "encryptedSavedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the encryptedSavedObjects plugin -date: 2023-02-23 +date: 2023-02-28 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 d3e819c70ed2f..b2441359f4430 100644 --- a/api_docs/enterprise_search.mdx +++ b/api_docs/enterprise_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/enterpriseSearch title: "enterpriseSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the enterpriseSearch plugin -date: 2023-02-23 +date: 2023-02-28 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 2ef981cb5eef8..2001eb24562a0 100644 --- a/api_docs/es_ui_shared.mdx +++ b/api_docs/es_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esUiShared title: "esUiShared" image: https://source.unsplash.com/400x175/?github description: API docs for the esUiShared plugin -date: 2023-02-23 +date: 2023-02-28 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 4a502d922eadc..b8fda3d5517fc 100644 --- a/api_docs/event_annotation.mdx +++ b/api_docs/event_annotation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotation title: "eventAnnotation" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotation plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation'] --- import eventAnnotationObj from './event_annotation.devdocs.json'; diff --git a/api_docs/event_log.devdocs.json b/api_docs/event_log.devdocs.json index 960b7a226d30c..ef90f20d24aa0 100644 --- a/api_docs/event_log.devdocs.json +++ b/api_docs/event_log.devdocs.json @@ -1514,7 +1514,7 @@ "label": "data", "description": [], "signature": [ - "(Readonly<{ log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; message?: string | undefined; tags?: string[] | undefined; rule?: Readonly<{ id?: string | undefined; name?: string | undefined; description?: string | undefined; category?: string | undefined; version?: string | undefined; uuid?: string | undefined; license?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; } & {}> | undefined; kibana?: Readonly<{ action?: Readonly<{ id?: string | undefined; name?: string | undefined; execution?: Readonly<{ uuid?: string | undefined; } & {}> | undefined; } & {}> | undefined; alerting?: Readonly<{ outcome?: string | undefined; summary?: Readonly<{ recovered?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; new?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; ongoing?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; } & {}> | undefined; status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ uuid?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: string | number | undefined; number_of_generated_actions?: string | number | undefined; alert_counts?: Readonly<{ recovered?: string | number | undefined; active?: string | number | undefined; new?: string | number | undefined; } & {}> | undefined; number_of_searches?: string | number | undefined; total_indexing_duration_ms?: string | number | undefined; es_search_duration_ms?: string | number | undefined; total_search_duration_ms?: string | number | undefined; execution_gap_duration_s?: string | number | undefined; rule_type_run_duration_ms?: string | number | undefined; process_alerts_duration_ms?: string | number | undefined; trigger_actions_duration_ms?: string | number | undefined; process_rule_duration_ms?: string | number | undefined; claim_to_start_duration_ms?: string | number | undefined; prepare_rule_duration_ms?: string | number | undefined; total_run_duration_ms?: string | number | undefined; total_enrichment_duration_ms?: string | number | undefined; } & {}> | undefined; status?: string | undefined; status_order?: string | number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; flapping?: boolean | undefined; } & {}> | undefined; version?: string | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: string | number | undefined; scheduled?: string | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; rel?: string | undefined; namespace?: string | undefined; type_id?: string | undefined; space_agnostic?: boolean | undefined; } & {}>[] | undefined; space_ids?: string[] | undefined; } & {}> | undefined; event?: Readonly<{ type?: string[] | undefined; reason?: string | undefined; action?: string | undefined; id?: string | undefined; start?: string | undefined; end?: string | undefined; category?: string[] | undefined; outcome?: string | undefined; code?: string | undefined; url?: string | undefined; created?: string | undefined; severity?: string | number | undefined; duration?: string | number | undefined; original?: string | undefined; dataset?: string | undefined; hash?: string | undefined; ingested?: string | undefined; kind?: string | undefined; module?: string | undefined; provider?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: string | number | undefined; timezone?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; } & {}> | undefined)[]" + "(Readonly<{ log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; message?: string | undefined; tags?: string[] | undefined; rule?: Readonly<{ id?: string | undefined; name?: string | undefined; description?: string | undefined; category?: string | undefined; version?: string | undefined; uuid?: string | undefined; license?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; } & {}> | undefined; kibana?: Readonly<{ action?: Readonly<{ id?: string | undefined; name?: string | undefined; execution?: Readonly<{ uuid?: string | undefined; } & {}> | undefined; } & {}> | undefined; alerting?: Readonly<{ outcome?: string | undefined; summary?: Readonly<{ recovered?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; new?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; ongoing?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; } & {}> | undefined; status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ uuid?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: string | number | undefined; number_of_generated_actions?: string | number | undefined; alert_counts?: Readonly<{ recovered?: string | number | undefined; active?: string | number | undefined; new?: string | number | undefined; } & {}> | undefined; number_of_searches?: string | number | undefined; total_indexing_duration_ms?: string | number | undefined; es_search_duration_ms?: string | number | undefined; total_search_duration_ms?: string | number | undefined; execution_gap_duration_s?: string | number | undefined; rule_type_run_duration_ms?: string | number | undefined; process_alerts_duration_ms?: string | number | undefined; trigger_actions_duration_ms?: string | number | undefined; process_rule_duration_ms?: string | number | undefined; claim_to_start_duration_ms?: string | number | undefined; prepare_rule_duration_ms?: string | number | undefined; total_run_duration_ms?: string | number | undefined; total_enrichment_duration_ms?: string | number | undefined; } & {}> | undefined; status?: string | undefined; status_order?: string | number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; flapping?: boolean | undefined; } & {}> | undefined; version?: string | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: string | number | undefined; scheduled?: string | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; rel?: string | undefined; namespace?: string | undefined; type_id?: string | undefined; space_agnostic?: boolean | undefined; } & {}>[] | undefined; space_ids?: string[] | undefined; } & {}> | undefined; event?: Readonly<{ type?: string[] | undefined; reason?: string | undefined; action?: string | undefined; id?: string | undefined; start?: string | undefined; end?: string | undefined; category?: string[] | undefined; outcome?: string | undefined; code?: string | undefined; url?: string | undefined; created?: string | undefined; severity?: string | number | undefined; duration?: string | number | undefined; original?: string | undefined; dataset?: string | undefined; hash?: string | undefined; ingested?: string | undefined; kind?: string | undefined; module?: string | undefined; provider?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: string | number | undefined; timezone?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; } & {}> | undefined)[]" ], "path": "x-pack/plugins/event_log/server/es/cluster_client_adapter.ts", "deprecated": false, @@ -1534,7 +1534,7 @@ "label": "IEvent", "description": [], "signature": [ - "DeepPartial | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; message?: string | undefined; tags?: string[] | undefined; rule?: Readonly<{ id?: string | undefined; name?: string | undefined; description?: string | undefined; category?: string | undefined; version?: string | undefined; uuid?: string | undefined; license?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; } & {}> | undefined; kibana?: Readonly<{ action?: Readonly<{ id?: string | undefined; name?: string | undefined; execution?: Readonly<{ uuid?: string | undefined; } & {}> | undefined; } & {}> | undefined; alerting?: Readonly<{ outcome?: string | undefined; summary?: Readonly<{ recovered?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; new?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; ongoing?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; } & {}> | undefined; status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ uuid?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: string | number | undefined; number_of_generated_actions?: string | number | undefined; alert_counts?: Readonly<{ recovered?: string | number | undefined; active?: string | number | undefined; new?: string | number | undefined; } & {}> | undefined; number_of_searches?: string | number | undefined; total_indexing_duration_ms?: string | number | undefined; es_search_duration_ms?: string | number | undefined; total_search_duration_ms?: string | number | undefined; execution_gap_duration_s?: string | number | undefined; rule_type_run_duration_ms?: string | number | undefined; process_alerts_duration_ms?: string | number | undefined; trigger_actions_duration_ms?: string | number | undefined; process_rule_duration_ms?: string | number | undefined; claim_to_start_duration_ms?: string | number | undefined; prepare_rule_duration_ms?: string | number | undefined; total_run_duration_ms?: string | number | undefined; total_enrichment_duration_ms?: string | number | undefined; } & {}> | undefined; status?: string | undefined; status_order?: string | number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; flapping?: boolean | undefined; } & {}> | undefined; version?: string | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: string | number | undefined; scheduled?: string | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; rel?: string | undefined; namespace?: string | undefined; type_id?: string | undefined; space_agnostic?: boolean | undefined; } & {}>[] | undefined; space_ids?: string[] | undefined; } & {}> | undefined; event?: Readonly<{ type?: string[] | undefined; reason?: string | undefined; action?: string | undefined; id?: string | undefined; start?: string | undefined; end?: string | undefined; category?: string[] | undefined; outcome?: string | undefined; code?: string | undefined; url?: string | undefined; created?: string | undefined; severity?: string | number | undefined; duration?: string | number | undefined; original?: string | undefined; dataset?: string | undefined; hash?: string | undefined; ingested?: string | undefined; kind?: string | undefined; module?: string | undefined; provider?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: string | number | undefined; timezone?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; } & {}>>> | undefined" + "DeepPartial | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; message?: string | undefined; tags?: string[] | undefined; rule?: Readonly<{ id?: string | undefined; name?: string | undefined; description?: string | undefined; category?: string | undefined; version?: string | undefined; uuid?: string | undefined; license?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; } & {}> | undefined; kibana?: Readonly<{ action?: Readonly<{ id?: string | undefined; name?: string | undefined; execution?: Readonly<{ uuid?: string | undefined; } & {}> | undefined; } & {}> | undefined; alerting?: Readonly<{ outcome?: string | undefined; summary?: Readonly<{ recovered?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; new?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; ongoing?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; } & {}> | undefined; status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ uuid?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: string | number | undefined; number_of_generated_actions?: string | number | undefined; alert_counts?: Readonly<{ recovered?: string | number | undefined; active?: string | number | undefined; new?: string | number | undefined; } & {}> | undefined; number_of_searches?: string | number | undefined; total_indexing_duration_ms?: string | number | undefined; es_search_duration_ms?: string | number | undefined; total_search_duration_ms?: string | number | undefined; execution_gap_duration_s?: string | number | undefined; rule_type_run_duration_ms?: string | number | undefined; process_alerts_duration_ms?: string | number | undefined; trigger_actions_duration_ms?: string | number | undefined; process_rule_duration_ms?: string | number | undefined; claim_to_start_duration_ms?: string | number | undefined; prepare_rule_duration_ms?: string | number | undefined; total_run_duration_ms?: string | number | undefined; total_enrichment_duration_ms?: string | number | undefined; } & {}> | undefined; status?: string | undefined; status_order?: string | number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; flapping?: boolean | undefined; } & {}> | undefined; version?: string | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: string | number | undefined; scheduled?: string | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; rel?: string | undefined; namespace?: string | undefined; type_id?: string | undefined; space_agnostic?: boolean | undefined; } & {}>[] | undefined; space_ids?: string[] | undefined; } & {}> | undefined; event?: Readonly<{ type?: string[] | undefined; reason?: string | undefined; action?: string | undefined; id?: string | undefined; start?: string | undefined; end?: string | undefined; category?: string[] | undefined; outcome?: string | undefined; code?: string | undefined; url?: string | undefined; created?: string | undefined; severity?: string | number | undefined; duration?: string | number | undefined; original?: string | undefined; dataset?: string | undefined; hash?: string | undefined; ingested?: string | undefined; kind?: string | undefined; module?: string | undefined; provider?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: string | number | undefined; timezone?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; } & {}>>> | undefined" ], "path": "x-pack/plugins/event_log/generated/schemas.ts", "deprecated": false, @@ -1549,7 +1549,7 @@ "label": "IValidatedEvent", "description": [], "signature": [ - "Readonly<{ log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; message?: string | undefined; tags?: string[] | undefined; rule?: Readonly<{ id?: string | undefined; name?: string | undefined; description?: string | undefined; category?: string | undefined; version?: string | undefined; uuid?: string | undefined; license?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; } & {}> | undefined; kibana?: Readonly<{ action?: Readonly<{ id?: string | undefined; name?: string | undefined; execution?: Readonly<{ uuid?: string | undefined; } & {}> | undefined; } & {}> | undefined; alerting?: Readonly<{ outcome?: string | undefined; summary?: Readonly<{ recovered?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; new?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; ongoing?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; } & {}> | undefined; status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ uuid?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: string | number | undefined; number_of_generated_actions?: string | number | undefined; alert_counts?: Readonly<{ recovered?: string | number | undefined; active?: string | number | undefined; new?: string | number | undefined; } & {}> | undefined; number_of_searches?: string | number | undefined; total_indexing_duration_ms?: string | number | undefined; es_search_duration_ms?: string | number | undefined; total_search_duration_ms?: string | number | undefined; execution_gap_duration_s?: string | number | undefined; rule_type_run_duration_ms?: string | number | undefined; process_alerts_duration_ms?: string | number | undefined; trigger_actions_duration_ms?: string | number | undefined; process_rule_duration_ms?: string | number | undefined; claim_to_start_duration_ms?: string | number | undefined; prepare_rule_duration_ms?: string | number | undefined; total_run_duration_ms?: string | number | undefined; total_enrichment_duration_ms?: string | number | undefined; } & {}> | undefined; status?: string | undefined; status_order?: string | number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; flapping?: boolean | undefined; } & {}> | undefined; version?: string | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: string | number | undefined; scheduled?: string | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; rel?: string | undefined; namespace?: string | undefined; type_id?: string | undefined; space_agnostic?: boolean | undefined; } & {}>[] | undefined; space_ids?: string[] | undefined; } & {}> | undefined; event?: Readonly<{ type?: string[] | undefined; reason?: string | undefined; action?: string | undefined; id?: string | undefined; start?: string | undefined; end?: string | undefined; category?: string[] | undefined; outcome?: string | undefined; code?: string | undefined; url?: string | undefined; created?: string | undefined; severity?: string | number | undefined; duration?: string | number | undefined; original?: string | undefined; dataset?: string | undefined; hash?: string | undefined; ingested?: string | undefined; kind?: string | undefined; module?: string | undefined; provider?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: string | number | undefined; timezone?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; } & {}> | undefined" + "Readonly<{ log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; message?: string | undefined; tags?: string[] | undefined; rule?: Readonly<{ id?: string | undefined; name?: string | undefined; description?: string | undefined; category?: string | undefined; version?: string | undefined; uuid?: string | undefined; license?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; } & {}> | undefined; kibana?: Readonly<{ action?: Readonly<{ id?: string | undefined; name?: string | undefined; execution?: Readonly<{ uuid?: string | undefined; } & {}> | undefined; } & {}> | undefined; alerting?: Readonly<{ outcome?: string | undefined; summary?: Readonly<{ recovered?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; new?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; ongoing?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; } & {}> | undefined; status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ uuid?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: string | number | undefined; number_of_generated_actions?: string | number | undefined; alert_counts?: Readonly<{ recovered?: string | number | undefined; active?: string | number | undefined; new?: string | number | undefined; } & {}> | undefined; number_of_searches?: string | number | undefined; total_indexing_duration_ms?: string | number | undefined; es_search_duration_ms?: string | number | undefined; total_search_duration_ms?: string | number | undefined; execution_gap_duration_s?: string | number | undefined; rule_type_run_duration_ms?: string | number | undefined; process_alerts_duration_ms?: string | number | undefined; trigger_actions_duration_ms?: string | number | undefined; process_rule_duration_ms?: string | number | undefined; claim_to_start_duration_ms?: string | number | undefined; prepare_rule_duration_ms?: string | number | undefined; total_run_duration_ms?: string | number | undefined; total_enrichment_duration_ms?: string | number | undefined; } & {}> | undefined; status?: string | undefined; status_order?: string | number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; flapping?: boolean | undefined; } & {}> | undefined; version?: string | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: string | number | undefined; scheduled?: string | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; rel?: string | undefined; namespace?: string | undefined; type_id?: string | undefined; space_agnostic?: boolean | undefined; } & {}>[] | undefined; space_ids?: string[] | undefined; } & {}> | undefined; event?: Readonly<{ type?: string[] | undefined; reason?: string | undefined; action?: string | undefined; id?: string | undefined; start?: string | undefined; end?: string | undefined; category?: string[] | undefined; outcome?: string | undefined; code?: string | undefined; url?: string | undefined; created?: string | undefined; severity?: string | number | undefined; duration?: string | number | undefined; original?: string | undefined; dataset?: string | undefined; hash?: string | undefined; ingested?: string | undefined; kind?: string | undefined; module?: string | undefined; provider?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: string | number | undefined; timezone?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; } & {}> | undefined" ], "path": "x-pack/plugins/event_log/generated/schemas.ts", "deprecated": false, diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx index cdc9adc5276d2..dbada9b2dfca3 100644 --- a/api_docs/event_log.mdx +++ b/api_docs/event_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventLog title: "eventLog" image: https://source.unsplash.com/400x175/?github description: API docs for the eventLog plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] --- import eventLogObj from './event_log.devdocs.json'; diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx index 20b6b0a6517ab..6622e021130e7 100644 --- a/api_docs/expression_error.mdx +++ b/api_docs/expression_error.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionError title: "expressionError" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionError plugin -date: 2023-02-23 +date: 2023-02-28 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 64bf958d705a3..8b15b75e8abbd 100644 --- a/api_docs/expression_gauge.mdx +++ b/api_docs/expression_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionGauge title: "expressionGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionGauge plugin -date: 2023-02-23 +date: 2023-02-28 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 2ace4d16b4857..908f94186daf1 100644 --- a/api_docs/expression_heatmap.mdx +++ b/api_docs/expression_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionHeatmap title: "expressionHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionHeatmap plugin -date: 2023-02-23 +date: 2023-02-28 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 2d178beeea861..3765e915e81df 100644 --- a/api_docs/expression_image.mdx +++ b/api_docs/expression_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionImage title: "expressionImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionImage plugin -date: 2023-02-23 +date: 2023-02-28 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 5f86234be73f9..654925d8dc2ce 100644 --- a/api_docs/expression_legacy_metric_vis.mdx +++ b/api_docs/expression_legacy_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionLegacyMetricVis title: "expressionLegacyMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionLegacyMetricVis plugin -date: 2023-02-23 +date: 2023-02-28 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 1593f37078f67..668becba901ef 100644 --- a/api_docs/expression_metric.mdx +++ b/api_docs/expression_metric.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetric title: "expressionMetric" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetric plugin -date: 2023-02-23 +date: 2023-02-28 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 4714e200c5048..b0dc76f0b6f76 100644 --- a/api_docs/expression_metric_vis.mdx +++ b/api_docs/expression_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetricVis title: "expressionMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetricVis plugin -date: 2023-02-23 +date: 2023-02-28 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 79b0e44197b4e..fd80d1a9b4784 100644 --- a/api_docs/expression_partition_vis.mdx +++ b/api_docs/expression_partition_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionPartitionVis title: "expressionPartitionVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionPartitionVis plugin -date: 2023-02-23 +date: 2023-02-28 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 203305da35cf4..cc220ea04321b 100644 --- a/api_docs/expression_repeat_image.mdx +++ b/api_docs/expression_repeat_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRepeatImage title: "expressionRepeatImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRepeatImage plugin -date: 2023-02-23 +date: 2023-02-28 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 4b8a6856f4f50..fee9dda3d70c3 100644 --- a/api_docs/expression_reveal_image.mdx +++ b/api_docs/expression_reveal_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRevealImage title: "expressionRevealImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRevealImage plugin -date: 2023-02-23 +date: 2023-02-28 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 b75efbdc14c6d..bf5da43e550bc 100644 --- a/api_docs/expression_shape.mdx +++ b/api_docs/expression_shape.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionShape title: "expressionShape" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionShape plugin -date: 2023-02-23 +date: 2023-02-28 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 4c5cc41c17898..b982d403d0459 100644 --- a/api_docs/expression_tagcloud.mdx +++ b/api_docs/expression_tagcloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionTagcloud title: "expressionTagcloud" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionTagcloud plugin -date: 2023-02-23 +date: 2023-02-28 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 f79aa106906da..6a3d62bd53b91 100644 --- a/api_docs/expression_x_y.mdx +++ b/api_docs/expression_x_y.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionXY title: "expressionXY" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionXY plugin -date: 2023-02-23 +date: 2023-02-28 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 5419bcae20be6..5fa1a549697f1 100644 --- a/api_docs/expressions.mdx +++ b/api_docs/expressions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressions title: "expressions" image: https://source.unsplash.com/400x175/?github description: API docs for the expressions plugin -date: 2023-02-23 +date: 2023-02-28 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 10067d548d791..d8ac383049b86 100644 --- a/api_docs/features.mdx +++ b/api_docs/features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/features title: "features" image: https://source.unsplash.com/400x175/?github description: API docs for the features plugin -date: 2023-02-23 +date: 2023-02-28 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 6988f85e08cd7..c48d1e9a662a6 100644 --- a/api_docs/field_formats.mdx +++ b/api_docs/field_formats.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fieldFormats title: "fieldFormats" image: https://source.unsplash.com/400x175/?github description: API docs for the fieldFormats plugin -date: 2023-02-23 +date: 2023-02-28 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 01a6c54ca63f6..8e93da50c1e5a 100644 --- a/api_docs/file_upload.mdx +++ b/api_docs/file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fileUpload title: "fileUpload" image: https://source.unsplash.com/400x175/?github description: API docs for the fileUpload plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fileUpload'] --- import fileUploadObj from './file_upload.devdocs.json'; diff --git a/api_docs/files.devdocs.json b/api_docs/files.devdocs.json index 462d19625454f..3c2201044be35 100644 --- a/api_docs/files.devdocs.json +++ b/api_docs/files.devdocs.json @@ -16,7 +16,13 @@ "signature": [ "FilesClient", " extends ", - "BaseFilesClient", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.BaseFilesClient", + "text": "BaseFilesClient" + }, "" ], "path": "src/plugins/files/common/files_client.ts", @@ -203,17 +209,77 @@ "text": "FilesMetrics" }, "; publicDownload: any; find: { files: ", - "FileJSON", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileJSON", + "text": "FileJSON" + }, "[]; total: number; }; bulkDelete: { succeeded: string[]; failed?: [id: string, reason: string][] | undefined; }; create: { file: ", - "FileJSON", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileJSON", + "text": "FileJSON" + }, "; }; delete: { ok: true; }; getById: { file: ", - "FileJSON", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileJSON", + "text": "FileJSON" + }, "; }; list: { files: ", - "FileJSON", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileJSON", + "text": "FileJSON" + }, "[]; total: number; }; update: { file: ", - "FileJSON", - "; }; upload: { ok: true; size: number; }; download: any; getDownloadHref: string; share: any; unshare: { ok: true; }; getShare: { share: FileShareJSON; }; listShares: { shares: FileShareJSON[]; }; getFileKind: ", - "FileKind", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileJSON", + "text": "FileJSON" + }, + "; }; upload: { ok: true; size: number; }; download: any; getDownloadHref: string; share: ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileShareJSON", + "text": "FileShareJSON" + }, + " & { token: string; }; unshare: { ok: true; }; getShare: { share: ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileShareJSON", + "text": "FileShareJSON" + }, + "; }; listShares: { shares: ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileShareJSON", + "text": "FileShareJSON" + }, + "[]; }; getFileKind: ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileKindBase", + "text": "FileKindBase" + }, "; }" ], "path": "src/plugins/files/common/files_client.ts", @@ -240,332 +306,222 @@ "text": "FilesMetrics" }, ">; publicDownload: (arg: Omit<{ token: string; fileName?: string | undefined; }, \"kind\">) => any; find: (arg: Omit<{ kind?: string | string[] | undefined; status?: string | string[] | undefined; extension?: string | string[] | undefined; name?: string | string[] | undefined; meta?: M | undefined; } & ", - "Pagination", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Pagination", + "text": "Pagination" + }, " & ", - "Abortable", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + }, ", \"kind\">) => Promise<{ files: ", - "FileJSON", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileJSON", + "text": "FileJSON" + }, "[]; total: number; }>; bulkDelete: (arg: Omit<{ ids: string[]; } & ", - "Abortable", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + }, ", \"kind\">) => Promise<{ succeeded: string[]; failed?: [id: string, reason: string][] | undefined; }>; create: (arg: Omit<{ name: string; meta?: M | undefined; alt?: string | undefined; mimeType?: string | undefined; kind: string; } & ", - "Abortable", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + }, ", \"kind\">) => Promise<{ file: ", - "FileJSON", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileJSON", + "text": "FileJSON" + }, "; }>; delete: (arg: Omit<{ id: string; kind: string; } & ", - "Abortable", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + }, ", \"kind\">) => Promise<{ ok: true; }>; getById: (arg: Omit<{ id: string; kind: string; } & ", - "Abortable", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + }, ", \"kind\">) => Promise<{ file: ", - "FileJSON", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileJSON", + "text": "FileJSON" + }, "; }>; list: (arg?: Omit<{ kind: string; status?: string | string[] | undefined; extension?: string | string[] | undefined; name?: string | string[] | undefined; meta?: M | undefined; } & ", - "Pagination", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Pagination", + "text": "Pagination" + }, " & ", - "Abortable", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + }, ", \"kind\"> | undefined) => Promise<{ files: ", - "FileJSON", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileJSON", + "text": "FileJSON" + }, "[]; total: number; }>; update: (arg: Omit<{ id: string; kind: string; name?: string | undefined; meta?: M | undefined; alt?: string | undefined; } & ", - "Abortable", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + }, ", \"kind\">) => Promise<{ file: ", - "FileJSON", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileJSON", + "text": "FileJSON" + }, "; }>; upload: (arg: Omit<{ id: string; body: unknown; kind: string; abortSignal?: AbortSignal | undefined; contentType?: string | undefined; selfDestructOnAbort?: boolean | undefined; } & ", - "Abortable", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + }, ", \"kind\">) => Promise<{ ok: true; size: number; }>; download: (arg: Omit<{ fileName?: string | undefined; id: string; kind: string; } & ", - "Abortable", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + }, ", \"kind\">) => Promise; getDownloadHref: (arg: Omit, \"id\" | \"fileKind\">, \"kind\">) => string; share: (arg: Omit<{ name?: string | undefined; validUntil?: number | undefined; fileId: string; kind: string; } & ", - "Abortable", - ", \"kind\">) => Promise; unshare: (arg: Omit<{ id: string; kind: string; } & ", - "Abortable", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + }, + ", \"kind\">) => Promise<", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileShareJSONWithToken", + "text": "FileShareJSONWithToken" + }, + ">; unshare: (arg: Omit<{ id: string; kind: string; } & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + }, ", \"kind\">) => Promise<{ ok: true; }>; getShare: (arg: Omit<{ id: string; kind: string; } & ", - "Abortable", - ", \"kind\">) => Promise<{ share: FileShareJSON; }>; listShares: (arg: Omit<{ forFileId?: string | undefined; kind: string; } & ", - "Pagination", - " & ", - "Abortable", - ", \"kind\">) => Promise<{ shares: FileShareJSON[]; }>; getFileKind: (arg: Omit) => ", - "FileKind", - "; }" - ], - "path": "src/plugins/files/common/files_client.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - } - ], - "objects": [ - { - "parentPluginId": "files", - "id": "def-public.defaultImageFileKind", - "type": "Object", - "tags": [], - "label": "defaultImageFileKind", - "description": [ - "\nA file kind that is available to all plugins to use for uploading images\nintended to be reused across Kibana." - ], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ { - "parentPluginId": "files", - "id": "def-public.defaultImageFileKind.id", - "type": "string", - "tags": [], - "label": "id", - "description": [], - "signature": [ - "\"defaultImage\"" - ], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" }, + ", \"kind\">) => Promise<{ share: ", { - "parentPluginId": "files", - "id": "def-public.defaultImageFileKind.maxSizeBytes", - "type": "number", - "tags": [], - "label": "maxSizeBytes", - "description": [], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileShareJSON", + "text": "FileShareJSON" }, + "; }>; listShares: (arg: Omit<{ forFileId?: string | undefined; kind: string; } & ", { - "parentPluginId": "files", - "id": "def-public.defaultImageFileKind.blobStoreSettings", - "type": "Object", - "tags": [], - "label": "blobStoreSettings", - "description": [], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false, - "children": [] + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Pagination", + "text": "Pagination" }, + " & ", { - "parentPluginId": "files", - "id": "def-public.defaultImageFileKind.allowedMimeTypes", - "type": "Array", - "tags": [], - "label": "allowedMimeTypes", - "description": [ - "// tried using \"image/*\" but it did not work with the HTTP endpoint (got 415 Unsupported Media Type)" - ], - "signature": [ - "string[]" - ], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" }, + ", \"kind\">) => Promise<{ shares: ", { - "parentPluginId": "files", - "id": "def-public.defaultImageFileKind.http", - "type": "Object", - "tags": [], - "label": "http", - "description": [], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "files", - "id": "def-public.defaultImageFileKind.http.create", - "type": "Object", - "tags": [], - "label": "create", - "description": [], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "files", - "id": "def-public.defaultImageFileKind.http.create.tags", - "type": "Array", - "tags": [], - "label": "tags", - "description": [], - "signature": [ - "string[]" - ], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false - } - ] - }, - { - "parentPluginId": "files", - "id": "def-public.defaultImageFileKind.http.delete", - "type": "Object", - "tags": [], - "label": "delete", - "description": [], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "files", - "id": "def-public.defaultImageFileKind.http.delete.tags", - "type": "Array", - "tags": [], - "label": "tags", - "description": [], - "signature": [ - "string[]" - ], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false - } - ] - }, - { - "parentPluginId": "files", - "id": "def-public.defaultImageFileKind.http.download", - "type": "Object", - "tags": [], - "label": "download", - "description": [], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "files", - "id": "def-public.defaultImageFileKind.http.download.tags", - "type": "Array", - "tags": [], - "label": "tags", - "description": [], - "signature": [ - "string[]" - ], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false - } - ] - }, - { - "parentPluginId": "files", - "id": "def-public.defaultImageFileKind.http.getById", - "type": "Object", - "tags": [], - "label": "getById", - "description": [], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "files", - "id": "def-public.defaultImageFileKind.http.getById.tags", - "type": "Array", - "tags": [], - "label": "tags", - "description": [], - "signature": [ - "string[]" - ], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false - } - ] - }, - { - "parentPluginId": "files", - "id": "def-public.defaultImageFileKind.http.list", - "type": "Object", - "tags": [], - "label": "list", - "description": [], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "files", - "id": "def-public.defaultImageFileKind.http.list.tags", - "type": "Array", - "tags": [], - "label": "tags", - "description": [], - "signature": [ - "string[]" - ], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false - } - ] - }, - { - "parentPluginId": "files", - "id": "def-public.defaultImageFileKind.http.share", - "type": "Object", - "tags": [], - "label": "share", - "description": [], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "files", - "id": "def-public.defaultImageFileKind.http.share.tags", - "type": "Array", - "tags": [], - "label": "tags", - "description": [], - "signature": [ - "string[]" - ], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false - } - ] - }, - { - "parentPluginId": "files", - "id": "def-public.defaultImageFileKind.http.update", - "type": "Object", - "tags": [], - "label": "update", - "description": [], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "files", - "id": "def-public.defaultImageFileKind.http.update.tags", - "type": "Array", - "tags": [], - "label": "tags", - "description": [], - "signature": [ - "string[]" - ], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false - } - ] - } - ] - } + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileShareJSON", + "text": "FileShareJSON" + }, + "[]; }>; getFileKind: (arg: Omit) => ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileKindBase", + "text": "FileKindBase" + }, + "; }" ], + "path": "src/plugins/files/common/files_client.ts", + "deprecated": false, + "trackAdoption": false, "initialIsOpen": false } ], + "objects": [], "setup": { "parentPluginId": "files", "id": "def-public.FilesSetup", @@ -588,7 +544,7 @@ ], "label": "filesClientFactory", "description": [ - "\nA factory for creating an {@link FilesClient} instance. This requires a\nregistered {@link FileKind}.\n" + "\nA factory for creating an {@link FilesClient} instance. This requires a\nregistered {@link FileKindBrowser}.\n" ], "signature": [ "FilesClientFactory" @@ -618,7 +574,13 @@ ], "signature": [ "(fileKind: ", - "FileKind", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileKindBrowser", + "text": "FileKindBrowser" + }, ") => void" ], "path": "src/plugins/files/public/plugin.ts", @@ -635,7 +597,13 @@ "- the file kind to register" ], "signature": [ - "FileKind" + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileKindBrowser", + "text": "FileKindBrowser" + } ], "path": "src/plugins/files/public/plugin.ts", "deprecated": false, @@ -779,7 +747,7 @@ "tags": [], "label": "elasticsearchClient", "description": [ - "\nAn elasticsearch client that will be used to interact with the cluster" + "\nAn elasticsearch client that will be used to interact with the cluster." ], "signature": [ "{ create: { (this: That, params: ", @@ -1981,7 +1949,7 @@ "tags": [], "label": "maxSizeBytes", "description": [ - "\nThe maximum file size to be write" + "\nThe maximum file size to be written." ], "signature": [ "number | undefined" @@ -1997,7 +1965,7 @@ "tags": [], "label": "logger", "description": [ - "\nA logger for debuggin purposes" + "\nA logger for debugging purposes." ], "signature": [ { @@ -2615,9 +2583,9 @@ "ShareArgs", ") => Promise<", { - "pluginId": "files", + "pluginId": "@kbn/shared-ux-file-types", "scope": "common", - "docId": "kibFilesPluginApi", + "docId": "kibKbnSharedUxFileTypesPluginApi", "section": "def-common.FileShareJSONWithToken", "text": "FileShareJSONWithToken" }, @@ -2706,9 +2674,9 @@ }, ") => Promise<{ shares: ", { - "pluginId": "files", + "pluginId": "@kbn/shared-ux-file-types", "scope": "common", - "docId": "kibFilesPluginApi", + "docId": "kibKbnSharedUxFileTypesPluginApi", "section": "def-common.FileShareJSON", "text": "FileShareJSON" }, @@ -2793,9 +2761,21 @@ ], "signature": [ "Required> & ", - "BaseFileMetadata", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.BaseFileMetadata", + "text": "BaseFileMetadata" + }, " & { FileKind: string; Meta?: M | undefined; }" ], "path": "src/plugins/files/server/file_client/file_metadata_client/file_metadata_client.ts", @@ -3514,7 +3494,13 @@ "text": "FindFileArgs" }, ") => Promise<{ files: ", - "FileJSON", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileJSON", + "text": "FileJSON" + }, "[]; total: number; }>" ], "path": "src/plugins/files/server/file_service/file_service.ts", @@ -3559,9 +3545,9 @@ "signature": [ "(arg: IdArg) => Promise<", { - "pluginId": "files", + "pluginId": "@kbn/shared-ux-file-types", "scope": "common", - "docId": "kibFilesPluginApi", + "docId": "kibKbnSharedUxFileTypesPluginApi", "section": "def-common.FileShareJSON", "text": "FileShareJSON" }, @@ -3610,9 +3596,9 @@ }, ") => Promise<{ shares: ", { - "pluginId": "files", + "pluginId": "@kbn/shared-ux-file-types", "scope": "common", - "docId": "kibFilesPluginApi", + "docId": "kibKbnSharedUxFileTypesPluginApi", "section": "def-common.FileShareJSON", "text": "FileShareJSON" }, @@ -3667,9 +3653,9 @@ }, ") => Promise<", { - "pluginId": "files", + "pluginId": "@kbn/shared-ux-file-types", "scope": "common", - "docId": "kibFilesPluginApi", + "docId": "kibKbnSharedUxFileTypesPluginApi", "section": "def-common.FileShare", "text": "FileShare" }, @@ -3837,9 +3823,9 @@ "signature": [ "(arg: IdArg) => Promise<", { - "pluginId": "files", + "pluginId": "@kbn/shared-ux-file-types", "scope": "common", - "docId": "kibFilesPluginApi", + "docId": "kibKbnSharedUxFileTypesPluginApi", "section": "def-common.FileShareJSON", "text": "FileShareJSON" }, @@ -3889,9 +3875,9 @@ }, ") => Promise<{ shares: ", { - "pluginId": "files", + "pluginId": "@kbn/shared-ux-file-types", "scope": "common", - "docId": "kibFilesPluginApi", + "docId": "kibKbnSharedUxFileTypesPluginApi", "section": "def-common.FileShareJSON", "text": "FileShareJSON" }, @@ -3947,9 +3933,9 @@ }, ") => Promise<", { - "pluginId": "files", + "pluginId": "@kbn/shared-ux-file-types", "scope": "common", - "docId": "kibFilesPluginApi", + "docId": "kibKbnSharedUxFileTypesPluginApi", "section": "def-common.FileShare", "text": "FileShare" }, @@ -4404,7 +4390,13 @@ ], "signature": [ "{ name?: string | undefined; created?: string | undefined; Status?: \"AWAITING_UPLOAD\" | \"UPLOADING\" | \"READY\" | \"UPLOAD_ERROR\" | \"DELETED\" | undefined; Updated?: string | undefined; mime_type?: string | undefined; size?: number | undefined; hash?: { [hashName: string]: string | undefined; md5?: string | undefined; sha1?: string | undefined; sha256?: string | undefined; sha384?: string | undefined; sha512?: string | undefined; ssdeep?: string | undefined; tlsh?: string | undefined; } | undefined; user?: { name?: string | undefined; id?: string | undefined; } | undefined; extension?: string | undefined; Alt?: string | undefined; ChunkSize?: number | undefined; Compression?: ", - "FileCompression", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileCompression", + "text": "FileCompression" + }, " | undefined; FileKind?: string | undefined; Meta?: M | undefined; }" ], "path": "src/plugins/files/server/file_client/file_metadata_client/file_metadata_client.ts", @@ -4569,7 +4561,13 @@ ], "signature": [ "(fileKind: ", - "FileKind", + { + "pluginId": "files", + "scope": "common", + "docId": "kibFilesPluginApi", + "section": "def-common.FileKind", + "text": "FileKind" + }, ") => void" ], "path": "src/plugins/files/server/types.ts", @@ -4587,7 +4585,13 @@ "- the file kind to register" ], "signature": [ - "FileKind" + { + "pluginId": "files", + "scope": "common", + "docId": "kibFilesPluginApi", + "section": "def-common.FileKind", + "text": "FileKind" + } ], "path": "src/plugins/files/server/types.ts", "deprecated": false, @@ -4726,7 +4730,13 @@ "\nFile metadata in camelCase form." ], "signature": [ - "FileJSON", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileJSON", + "text": "FileJSON" + }, "" ], "path": "src/plugins/files/common/types.ts", @@ -4921,9 +4931,9 @@ }, " | undefined) => Promise<", { - "pluginId": "files", + "pluginId": "@kbn/shared-ux-file-types", "scope": "common", - "docId": "kibFilesPluginApi", + "docId": "kibKbnSharedUxFileTypesPluginApi", "section": "def-common.FileShareJSONWithToken", "text": "FileShareJSONWithToken" }, @@ -4972,9 +4982,9 @@ "signature": [ "() => Promise<", { - "pluginId": "files", + "pluginId": "@kbn/shared-ux-file-types", "scope": "common", - "docId": "kibFilesPluginApi", + "docId": "kibKbnSharedUxFileTypesPluginApi", "section": "def-common.FileShareJSON", "text": "FileShareJSON" }, @@ -5047,7 +5057,13 @@ ], "signature": [ "() => ", - "FileJSON", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileJSON", + "text": "FileJSON" + }, "" ], "path": "src/plugins/files/common/types.ts", @@ -5069,10 +5085,16 @@ "\nAttributes of a file that represent a serialised version of the file." ], "signature": [ - "FileJSON", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileJSON", + "text": "FileJSON" + }, "" ], - "path": "packages/shared-ux/file/types/index.d.ts", + "path": "packages/shared-ux/file/types/index.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -5085,7 +5107,7 @@ "description": [ "\nUnique file ID." ], - "path": "packages/shared-ux/file/types/index.d.ts", + "path": "packages/shared-ux/file/types/index.ts", "deprecated": false, "trackAdoption": false }, @@ -5098,7 +5120,7 @@ "description": [ "\nISO string of when this file was created" ], - "path": "packages/shared-ux/file/types/index.d.ts", + "path": "packages/shared-ux/file/types/index.ts", "deprecated": false, "trackAdoption": false }, @@ -5111,7 +5133,7 @@ "description": [ "\nISO string of when the file was updated" ], - "path": "packages/shared-ux/file/types/index.d.ts", + "path": "packages/shared-ux/file/types/index.ts", "deprecated": false, "trackAdoption": false }, @@ -5126,7 +5148,7 @@ "description": [ "\nFile name.\n" ], - "path": "packages/shared-ux/file/types/index.d.ts", + "path": "packages/shared-ux/file/types/index.ts", "deprecated": false, "trackAdoption": false }, @@ -5142,7 +5164,7 @@ "signature": [ "string | undefined" ], - "path": "packages/shared-ux/file/types/index.d.ts", + "path": "packages/shared-ux/file/types/index.ts", "deprecated": false, "trackAdoption": false }, @@ -5158,7 +5180,7 @@ "signature": [ "number | undefined" ], - "path": "packages/shared-ux/file/types/index.d.ts", + "path": "packages/shared-ux/file/types/index.ts", "deprecated": false, "trackAdoption": false }, @@ -5176,7 +5198,7 @@ "signature": [ "string | undefined" ], - "path": "packages/shared-ux/file/types/index.d.ts", + "path": "packages/shared-ux/file/types/index.ts", "deprecated": false, "trackAdoption": false }, @@ -5192,7 +5214,7 @@ "signature": [ "Meta | undefined" ], - "path": "packages/shared-ux/file/types/index.d.ts", + "path": "packages/shared-ux/file/types/index.ts", "deprecated": false, "trackAdoption": false }, @@ -5208,7 +5230,7 @@ "signature": [ "string | undefined" ], - "path": "packages/shared-ux/file/types/index.d.ts", + "path": "packages/shared-ux/file/types/index.ts", "deprecated": false, "trackAdoption": false }, @@ -5223,7 +5245,7 @@ "description": [ "\nA unique kind that governs various aspects of the file. A consumer of the\nfiles service must register a file kind and link their files to a specific\nkind.\n" ], - "path": "packages/shared-ux/file/types/index.d.ts", + "path": "packages/shared-ux/file/types/index.ts", "deprecated": false, "trackAdoption": false }, @@ -5239,7 +5261,7 @@ "signature": [ "\"AWAITING_UPLOAD\" | \"UPLOADING\" | \"READY\" | \"UPLOAD_ERROR\" | \"DELETED\"" ], - "path": "packages/shared-ux/file/types/index.d.ts", + "path": "packages/shared-ux/file/types/index.ts", "deprecated": false, "trackAdoption": false }, @@ -5255,7 +5277,7 @@ "signature": [ "{ name?: string | undefined; id?: string | undefined; } | undefined" ], - "path": "packages/shared-ux/file/types/index.d.ts", + "path": "packages/shared-ux/file/types/index.ts", "deprecated": false, "trackAdoption": false } @@ -5269,72 +5291,73 @@ "tags": [], "label": "FileKind", "description": [], - "path": "packages/shared-ux/file/types/index.d.ts", + "signature": [ + { + "pluginId": "files", + "scope": "common", + "docId": "kibFilesPluginApi", + "section": "def-common.FileKind", + "text": "FileKind" + }, + " extends ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileKindBase", + "text": "FileKindBase" + } + ], + "path": "src/plugins/files/common/types.ts", "deprecated": false, "trackAdoption": false, "children": [ - { - "parentPluginId": "files", - "id": "def-common.FileKind.id", - "type": "string", - "tags": [], - "label": "id", - "description": [ - "\nUnique file kind ID" - ], - "path": "packages/shared-ux/file/types/index.d.ts", - "deprecated": false, - "trackAdoption": false - }, { "parentPluginId": "files", "id": "def-common.FileKind.maxSizeBytes", - "type": "number", + "type": "CompoundType", "tags": [ "default" ], "label": "maxSizeBytes", "description": [ - "\nMaximum size, in bytes, a file of this kind can be.\n" + "\nMax file contents size, in bytes. Can be customized per file using the\n{@link FileJSON} object. This is enforced on the server-side as well as\nin the upload React component.\n" ], "signature": [ - "number | undefined" - ], - "path": "packages/shared-ux/file/types/index.d.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "files", - "id": "def-common.FileKind.allowedMimeTypes", - "type": "Array", - "tags": [ - "default" - ], - "label": "allowedMimeTypes", - "description": [ - "\nThe MIME type of the file content.\n" - ], - "signature": [ - "string[] | undefined" + "number | ((file: ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileJSON", + "text": "FileJSON" + }, + ") => number) | undefined" ], - "path": "packages/shared-ux/file/types/index.d.ts", + "path": "src/plugins/files/common/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "files", "id": "def-common.FileKind.blobStoreSettings", - "type": "Any", + "type": "Object", "tags": [], "label": "blobStoreSettings", "description": [ "\nBlob store specific settings that enable configuration of storage\ndetails." ], "signature": [ - "any" + { + "pluginId": "files", + "scope": "common", + "docId": "kibFilesPluginApi", + "section": "def-common.BlobStorageSettings", + "text": "BlobStorageSettings" + }, + " | undefined" ], - "path": "packages/shared-ux/file/types/index.d.ts", + "path": "src/plugins/files/common/types.ts", "deprecated": false, "trackAdoption": false }, @@ -5352,7 +5375,56 @@ "signature": [ "{ create?: HttpEndpointDefinition | undefined; update?: HttpEndpointDefinition | undefined; delete?: HttpEndpointDefinition | undefined; getById?: HttpEndpointDefinition | undefined; list?: HttpEndpointDefinition | undefined; download?: HttpEndpointDefinition | undefined; share?: HttpEndpointDefinition | undefined; }" ], - "path": "packages/shared-ux/file/types/index.d.ts", + "path": "src/plugins/files/common/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "files", + "id": "def-common.FileKindBrowser", + "type": "Interface", + "tags": [], + "label": "FileKindBrowser", + "description": [], + "signature": [ + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileKindBrowser", + "text": "FileKindBrowser" + }, + " extends ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileKindBase", + "text": "FileKindBase" + } + ], + "path": "packages/shared-ux/file/types/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "files", + "id": "def-common.FileKindBrowser.maxSizeBytes", + "type": "number", + "tags": [ + "default" + ], + "label": "maxSizeBytes", + "description": [ + "\nMax file contents size, in bytes, enforced for this file kind in the upload\ncomponent.\n" + ], + "signature": [ + "number | undefined" + ], + "path": "packages/shared-ux/file/types/index.ts", "deprecated": false, "trackAdoption": false } @@ -5368,7 +5440,7 @@ "description": [ "\nAttributes of a file that represent a serialised version of the file." ], - "path": "src/plugins/files/common/types.ts", + "path": "packages/shared-ux/file/types/sharing.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -5381,7 +5453,7 @@ "description": [ "\nUnique ID share instance" ], - "path": "src/plugins/files/common/types.ts", + "path": "packages/shared-ux/file/types/sharing.ts", "deprecated": false, "trackAdoption": false }, @@ -5394,7 +5466,7 @@ "description": [ "\nISO timestamp the share was created" ], - "path": "src/plugins/files/common/types.ts", + "path": "packages/shared-ux/file/types/sharing.ts", "deprecated": false, "trackAdoption": false }, @@ -5407,7 +5479,7 @@ "description": [ "\nUnix timestamp (in milliseconds) of when this share expires" ], - "path": "src/plugins/files/common/types.ts", + "path": "packages/shared-ux/file/types/sharing.ts", "deprecated": false, "trackAdoption": false }, @@ -5423,7 +5495,7 @@ "signature": [ "string | undefined" ], - "path": "src/plugins/files/common/types.ts", + "path": "packages/shared-ux/file/types/sharing.ts", "deprecated": false, "trackAdoption": false }, @@ -5436,7 +5508,7 @@ "description": [ "\nThe ID of the file this share is linked to" ], - "path": "src/plugins/files/common/types.ts", + "path": "packages/shared-ux/file/types/sharing.ts", "deprecated": false, "trackAdoption": false } @@ -5648,12 +5720,24 @@ ], "signature": [ "{ name?: string | undefined; mime_type?: string | undefined; created?: string | undefined; size?: number | undefined; hash?: { [hashName: string]: string | undefined; md5?: string | undefined; sha1?: string | undefined; sha256?: string | undefined; sha384?: string | undefined; sha512?: string | undefined; ssdeep?: string | undefined; tlsh?: string | undefined; } | undefined; user?: { name?: string | undefined; id?: string | undefined; } | undefined; extension?: string | undefined; Alt?: string | undefined; Updated?: string | undefined; Status?: ", - "FileStatus", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileStatus", + "text": "FileStatus" + }, " | undefined; ChunkSize?: number | undefined; Compression?: ", - "FileCompression", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileCompression", + "text": "FileCompression" + }, " | undefined; }" ], - "path": "packages/shared-ux/file/types/index.d.ts", + "path": "packages/shared-ux/file/types/index.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -5704,7 +5788,7 @@ "signature": [ "\"none\" | \"br\" | \"gzip\" | \"deflate\"" ], - "path": "packages/shared-ux/file/types/index.d.ts", + "path": "packages/shared-ux/file/types/index.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -5720,12 +5804,24 @@ ], "signature": [ "Required> & ", - "BaseFileMetadata", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.BaseFileMetadata", + "text": "BaseFileMetadata" + }, " & { FileKind: string; Meta?: Meta | undefined; }" ], - "path": "packages/shared-ux/file/types/index.d.ts", + "path": "packages/shared-ux/file/types/index.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -5748,7 +5844,13 @@ "text": "SavedObject" }, "<", - "FileMetadata", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileMetadata", + "text": "FileMetadata" + }, ">" ], "path": "src/plugins/files/common/types.ts", @@ -5768,7 +5870,7 @@ "signature": [ "{ created: string; token: string; name?: string | undefined; valid_until: number; }" ], - "path": "src/plugins/files/common/types.ts", + "path": "packages/shared-ux/file/types/sharing.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -5786,15 +5888,15 @@ ], "signature": [ { - "pluginId": "files", + "pluginId": "@kbn/shared-ux-file-types", "scope": "common", - "docId": "kibFilesPluginApi", + "docId": "kibKbnSharedUxFileTypesPluginApi", "section": "def-common.FileShareJSON", "text": "FileShareJSON" }, " & { token: string; }" ], - "path": "src/plugins/files/common/types.ts", + "path": "packages/shared-ux/file/types/sharing.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -5809,7 +5911,7 @@ "signature": [ "\"AWAITING_UPLOAD\" | \"UPLOADING\" | \"READY\" | \"UPLOAD_ERROR\" | \"DELETED\"" ], - "path": "packages/shared-ux/file/types/index.d.ts", + "path": "packages/shared-ux/file/types/index.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -5883,278 +5985,6 @@ "initialIsOpen": false } ], - "objects": [ - { - "parentPluginId": "files", - "id": "def-common.defaultImageFileKind", - "type": "Object", - "tags": [], - "label": "defaultImageFileKind", - "description": [ - "\nA file kind that is available to all plugins to use for uploading images\nintended to be reused across Kibana." - ], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "files", - "id": "def-common.defaultImageFileKind.id", - "type": "string", - "tags": [], - "label": "id", - "description": [], - "signature": [ - "\"defaultImage\"" - ], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "files", - "id": "def-common.defaultImageFileKind.maxSizeBytes", - "type": "number", - "tags": [], - "label": "maxSizeBytes", - "description": [], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "files", - "id": "def-common.defaultImageFileKind.blobStoreSettings", - "type": "Object", - "tags": [], - "label": "blobStoreSettings", - "description": [], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false, - "children": [] - }, - { - "parentPluginId": "files", - "id": "def-common.defaultImageFileKind.allowedMimeTypes", - "type": "Array", - "tags": [], - "label": "allowedMimeTypes", - "description": [ - "// tried using \"image/*\" but it did not work with the HTTP endpoint (got 415 Unsupported Media Type)" - ], - "signature": [ - "string[]" - ], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "files", - "id": "def-common.defaultImageFileKind.http", - "type": "Object", - "tags": [], - "label": "http", - "description": [], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "files", - "id": "def-common.defaultImageFileKind.http.create", - "type": "Object", - "tags": [], - "label": "create", - "description": [], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "files", - "id": "def-common.defaultImageFileKind.http.create.tags", - "type": "Array", - "tags": [], - "label": "tags", - "description": [], - "signature": [ - "string[]" - ], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false - } - ] - }, - { - "parentPluginId": "files", - "id": "def-common.defaultImageFileKind.http.delete", - "type": "Object", - "tags": [], - "label": "delete", - "description": [], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "files", - "id": "def-common.defaultImageFileKind.http.delete.tags", - "type": "Array", - "tags": [], - "label": "tags", - "description": [], - "signature": [ - "string[]" - ], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false - } - ] - }, - { - "parentPluginId": "files", - "id": "def-common.defaultImageFileKind.http.download", - "type": "Object", - "tags": [], - "label": "download", - "description": [], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "files", - "id": "def-common.defaultImageFileKind.http.download.tags", - "type": "Array", - "tags": [], - "label": "tags", - "description": [], - "signature": [ - "string[]" - ], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false - } - ] - }, - { - "parentPluginId": "files", - "id": "def-common.defaultImageFileKind.http.getById", - "type": "Object", - "tags": [], - "label": "getById", - "description": [], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "files", - "id": "def-common.defaultImageFileKind.http.getById.tags", - "type": "Array", - "tags": [], - "label": "tags", - "description": [], - "signature": [ - "string[]" - ], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false - } - ] - }, - { - "parentPluginId": "files", - "id": "def-common.defaultImageFileKind.http.list", - "type": "Object", - "tags": [], - "label": "list", - "description": [], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "files", - "id": "def-common.defaultImageFileKind.http.list.tags", - "type": "Array", - "tags": [], - "label": "tags", - "description": [], - "signature": [ - "string[]" - ], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false - } - ] - }, - { - "parentPluginId": "files", - "id": "def-common.defaultImageFileKind.http.share", - "type": "Object", - "tags": [], - "label": "share", - "description": [], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "files", - "id": "def-common.defaultImageFileKind.http.share.tags", - "type": "Array", - "tags": [], - "label": "tags", - "description": [], - "signature": [ - "string[]" - ], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false - } - ] - }, - { - "parentPluginId": "files", - "id": "def-common.defaultImageFileKind.http.update", - "type": "Object", - "tags": [], - "label": "update", - "description": [], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "files", - "id": "def-common.defaultImageFileKind.http.update.tags", - "type": "Array", - "tags": [], - "label": "tags", - "description": [], - "signature": [ - "string[]" - ], - "path": "src/plugins/files/common/default_image_file_kind.ts", - "deprecated": false, - "trackAdoption": false - } - ] - } - ] - } - ], - "initialIsOpen": false - } - ] + "objects": [] } } \ No newline at end of file diff --git a/api_docs/files.mdx b/api_docs/files.mdx index 046e2d41a854f..681850c64b442 100644 --- a/api_docs/files.mdx +++ b/api_docs/files.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/files title: "files" image: https://source.unsplash.com/400x175/?github description: API docs for the files plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'files'] --- import filesObj from './files.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sh | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 254 | 1 | 45 | 5 | +| 214 | 0 | 10 | 5 | ## Client @@ -31,9 +31,6 @@ Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sh ### Start -### Objects - - ### Interfaces @@ -59,9 +56,6 @@ Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sh ## Common -### Objects - - ### Interfaces diff --git a/api_docs/files_management.mdx b/api_docs/files_management.mdx index 4ca03d5470558..da7e5cf17aa8b 100644 --- a/api_docs/files_management.mdx +++ b/api_docs/files_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/filesManagement title: "filesManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the filesManagement plugin -date: 2023-02-23 +date: 2023-02-28 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 f9a49b8123b49..6b022a38b7bd6 100644 --- a/api_docs/fleet.devdocs.json +++ b/api_docs/fleet.devdocs.json @@ -25180,6 +25180,17 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "fleet", + "id": "def-common.EPM_API_ROUTES.VERIFICATION_KEY_ID", + "type": "string", + "tags": [], + "label": "VERIFICATION_KEY_ID", + "description": [], + "path": "x-pack/plugins/fleet/common/constants/routes.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "fleet", "id": "def-common.EPM_API_ROUTES.STATS_PATTERN", @@ -25238,6 +25249,22 @@ "deprecated": false, "trackAdoption": false, "children": [ + { + "parentPluginId": "fleet", + "id": "def-common.epmRouteService.getVerificationKeyIdPath", + "type": "Function", + "tags": [], + "label": "getVerificationKeyIdPath", + "description": [], + "signature": [ + "() => string" + ], + "path": "x-pack/plugins/fleet/common/services/routes.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, { "parentPluginId": "fleet", "id": "def-common.epmRouteService.getCategoriesPath", diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index f40b55b8dc7e3..e605c7ee935e0 100644 --- a/api_docs/fleet.mdx +++ b/api_docs/fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fleet title: "fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the fleet plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fleet'] --- import fleetObj from './fleet.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) for questi | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 1087 | 3 | 982 | 27 | +| 1089 | 3 | 984 | 27 | ## Client diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index e03a8d3f28f09..4cd3f5543c815 100644 --- a/api_docs/global_search.mdx +++ b/api_docs/global_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/globalSearch title: "globalSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the globalSearch plugin -date: 2023-02-23 +date: 2023-02-28 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 b9721524c64a5..ccd8dc54f268d 100644 --- a/api_docs/guided_onboarding.mdx +++ b/api_docs/guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/guidedOnboarding title: "guidedOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the guidedOnboarding plugin -date: 2023-02-23 +date: 2023-02-28 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 c90de57005ac1..6e9c53480fdae 100644 --- a/api_docs/home.mdx +++ b/api_docs/home.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/home title: "home" image: https://source.unsplash.com/400x175/?github description: API docs for the home plugin -date: 2023-02-23 +date: 2023-02-28 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 377e790d058eb..fa1b8a61ad548 100644 --- a/api_docs/image_embeddable.mdx +++ b/api_docs/image_embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/imageEmbeddable title: "imageEmbeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the imageEmbeddable plugin -date: 2023-02-23 +date: 2023-02-28 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 3a1e98511cfe3..bb61d5ca51b3f 100644 --- a/api_docs/index_lifecycle_management.mdx +++ b/api_docs/index_lifecycle_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexLifecycleManagement title: "indexLifecycleManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexLifecycleManagement plugin -date: 2023-02-23 +date: 2023-02-28 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 a7001bd8d7d4a..5dc3493807348 100644 --- a/api_docs/index_management.mdx +++ b/api_docs/index_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexManagement title: "indexManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexManagement plugin -date: 2023-02-23 +date: 2023-02-28 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 00055f4df737f..1bce234957b6d 100644 --- a/api_docs/infra.mdx +++ b/api_docs/infra.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/infra title: "infra" image: https://source.unsplash.com/400x175/?github description: API docs for the infra plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra'] --- import infraObj from './infra.devdocs.json'; diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx index 8d8c2654a4879..894e811cb6f8f 100644 --- a/api_docs/inspector.mdx +++ b/api_docs/inspector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/inspector title: "inspector" image: https://source.unsplash.com/400x175/?github description: API docs for the inspector plugin -date: 2023-02-23 +date: 2023-02-28 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 b7bf4086fa208..d575dd2ad8d69 100644 --- a/api_docs/interactive_setup.mdx +++ b/api_docs/interactive_setup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/interactiveSetup title: "interactiveSetup" image: https://source.unsplash.com/400x175/?github description: API docs for the interactiveSetup plugin -date: 2023-02-23 +date: 2023-02-28 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 d811a2b29210c..6daa5009ec5ce 100644 --- a/api_docs/kbn_ace.mdx +++ b/api_docs/kbn_ace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ace title: "@kbn/ace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ace plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ace'] --- import kbnAceObj from './kbn_ace.devdocs.json'; diff --git a/api_docs/kbn_aiops_components.mdx b/api_docs/kbn_aiops_components.mdx index 447b111811fce..938b5bd49c8b0 100644 --- a/api_docs/kbn_aiops_components.mdx +++ b/api_docs/kbn_aiops_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-components title: "@kbn/aiops-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-components plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] --- import kbnAiopsComponentsObj from './kbn_aiops_components.devdocs.json'; diff --git a/api_docs/kbn_aiops_utils.mdx b/api_docs/kbn_aiops_utils.mdx index 4270ea2280bac..15a61db6deb96 100644 --- a/api_docs/kbn_aiops_utils.mdx +++ b/api_docs/kbn_aiops_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-utils title: "@kbn/aiops-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-utils plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-utils'] --- import kbnAiopsUtilsObj from './kbn_aiops_utils.devdocs.json'; diff --git a/api_docs/kbn_alerts.mdx b/api_docs/kbn_alerts.mdx index b82428a3c68f3..317fbd1e1da7c 100644 --- a/api_docs/kbn_alerts.mdx +++ b/api_docs/kbn_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts title: "@kbn/alerts" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts'] --- import kbnAlertsObj from './kbn_alerts.devdocs.json'; diff --git a/api_docs/kbn_alerts_as_data_utils.devdocs.json b/api_docs/kbn_alerts_as_data_utils.devdocs.json new file mode 100644 index 0000000000000..b5390c5044148 --- /dev/null +++ b/api_docs/kbn_alerts_as_data_utils.devdocs.json @@ -0,0 +1,215 @@ +{ + "id": "@kbn/alerts-as-data-utils", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [ + { + "parentPluginId": "@kbn/alerts-as-data-utils", + "id": "def-common.FieldMap", + "type": "Interface", + "tags": [], + "label": "FieldMap", + "description": [], + "path": "packages/kbn-alerts-as-data-utils/src/field_maps/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/alerts-as-data-utils", + "id": "def-common.FieldMap.Unnamed", + "type": "IndexSignature", + "tags": [], + "label": "[key: string]: { type: string; required: boolean; array?: boolean | undefined; doc_values?: boolean | undefined; enabled?: boolean | undefined; format?: string | undefined; ignore_above?: number | undefined; ... 4 more ...; dynamic?: boolean | ... 1 more ... | undefined; }", + "description": [], + "signature": [ + "[key: string]: { type: string; required: boolean; array?: boolean | undefined; doc_values?: boolean | undefined; enabled?: boolean | undefined; format?: string | undefined; ignore_above?: number | undefined; multi_fields?: ", + { + "pluginId": "@kbn/alerts-as-data-utils", + "scope": "common", + "docId": "kibKbnAlertsAsDataUtilsPluginApi", + "section": "def-common.MultiField", + "text": "MultiField" + }, + "[] | undefined; index?: boolean | undefined; path?: string | undefined; scaling_factor?: number | undefined; dynamic?: boolean | \"strict\" | undefined; }" + ], + "path": "packages/kbn-alerts-as-data-utils/src/field_maps/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/alerts-as-data-utils", + "id": "def-common.MultiField", + "type": "Interface", + "tags": [], + "label": "MultiField", + "description": [], + "path": "packages/kbn-alerts-as-data-utils/src/field_maps/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/alerts-as-data-utils", + "id": "def-common.MultiField.flat_name", + "type": "string", + "tags": [], + "label": "flat_name", + "description": [], + "path": "packages/kbn-alerts-as-data-utils/src/field_maps/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/alerts-as-data-utils", + "id": "def-common.MultiField.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "packages/kbn-alerts-as-data-utils/src/field_maps/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/alerts-as-data-utils", + "id": "def-common.MultiField.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-alerts-as-data-utils/src/field_maps/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/alerts-as-data-utils", + "id": "def-common.AlertFieldMap", + "type": "Type", + "tags": [], + "label": "AlertFieldMap", + "description": [], + "signature": [ + "{ readonly \"kibana.alert.action_group\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.case_ids\": { readonly type: \"keyword\"; readonly array: true; readonly required: false; }; readonly \"kibana.alert.duration.us\": { readonly type: \"long\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.end\": { readonly type: \"date\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.flapping\": { readonly type: \"boolean\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.flapping_history\": { readonly type: \"boolean\"; readonly array: true; readonly required: false; }; readonly \"kibana.alert.instance.id\": { readonly type: \"keyword\"; readonly array: false; readonly required: true; }; readonly \"kibana.alert.last_detected\": { readonly type: \"date\"; readonly required: false; readonly array: false; }; readonly \"kibana.alert.reason\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.category\": { readonly type: \"keyword\"; readonly array: false; readonly required: true; }; readonly \"kibana.alert.rule.consumer\": { readonly type: \"keyword\"; readonly array: false; readonly required: true; }; readonly \"kibana.alert.rule.execution.uuid\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.name\": { readonly type: \"keyword\"; readonly array: false; readonly required: true; }; readonly \"kibana.alert.rule.parameters\": { readonly array: false; readonly type: \"flattened\"; readonly ignore_above: 4096; readonly required: false; }; readonly \"kibana.alert.rule.producer\": { readonly type: \"keyword\"; readonly array: false; readonly required: true; }; readonly \"kibana.alert.rule.tags\": { readonly type: \"keyword\"; readonly array: true; readonly required: false; }; readonly \"kibana.alert.rule.rule_type_id\": { readonly type: \"keyword\"; readonly array: false; readonly required: true; }; readonly \"kibana.alert.rule.uuid\": { readonly type: \"keyword\"; readonly array: false; readonly required: true; }; readonly \"kibana.alert.start\": { readonly type: \"date\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.status\": { readonly type: \"keyword\"; readonly array: false; readonly required: true; }; readonly \"kibana.alert.time_range\": { readonly type: \"date_range\"; readonly format: \"epoch_millis||strict_date_optional_time\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.uuid\": { readonly type: \"keyword\"; readonly array: false; readonly required: true; }; readonly \"kibana.alert.workflow_status\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.space_ids\": { readonly type: \"keyword\"; readonly array: true; readonly required: true; }; readonly \"@timestamp\": { readonly type: \"date\"; readonly required: true; readonly array: false; }; readonly \"kibana.version\": { readonly type: \"version\"; readonly array: false; readonly required: false; }; }" + ], + "path": "packages/kbn-alerts-as-data-utils/src/field_maps/alert_field_map.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/alerts-as-data-utils", + "id": "def-common.EcsFieldMap", + "type": "Type", + "tags": [], + "label": "EcsFieldMap", + "description": [], + "signature": [ + { + "pluginId": "@kbn/alerts-as-data-utils", + "scope": "common", + "docId": "kibKbnAlertsAsDataUtilsPluginApi", + "section": "def-common.FieldMap", + "text": "FieldMap" + } + ], + "path": "packages/kbn-alerts-as-data-utils/src/field_maps/ecs_field_map.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/alerts-as-data-utils", + "id": "def-common.LegacyAlertFieldMap", + "type": "Type", + "tags": [], + "label": "LegacyAlertFieldMap", + "description": [], + "signature": [ + "{ readonly \"kibana.alert.risk_score\": { readonly type: \"float\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.author\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.created_at\": { readonly type: \"date\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.created_by\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.description\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.enabled\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.from\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.interval\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.license\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.note\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.references\": { readonly type: \"keyword\"; readonly array: true; readonly required: false; }; readonly \"kibana.alert.rule.rule_id\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.rule_name_override\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.to\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.type\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.updated_at\": { readonly type: \"date\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.updated_by\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.version\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.severity\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.suppression.docs_count\": { readonly type: \"long\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.suppression.end\": { readonly type: \"date\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.suppression.terms.field\": { readonly type: \"keyword\"; readonly array: true; readonly required: false; }; readonly \"kibana.alert.suppression.start\": { readonly type: \"date\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.suppression.terms.value\": { readonly type: \"keyword\"; readonly array: true; readonly required: false; }; readonly \"kibana.alert.system_status\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.workflow_reason\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.workflow_user\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"ecs.version\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"event.action\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"event.kind\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly tags: { readonly type: \"keyword\"; readonly array: true; readonly required: false; }; }" + ], + "path": "packages/kbn-alerts-as-data-utils/src/field_maps/legacy_alert_field_map.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [ + { + "parentPluginId": "@kbn/alerts-as-data-utils", + "id": "def-common.alertFieldMap", + "type": "Object", + "tags": [], + "label": "alertFieldMap", + "description": [], + "signature": [ + "{ readonly \"kibana.alert.action_group\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.case_ids\": { readonly type: \"keyword\"; readonly array: true; readonly required: false; }; readonly \"kibana.alert.duration.us\": { readonly type: \"long\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.end\": { readonly type: \"date\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.flapping\": { readonly type: \"boolean\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.flapping_history\": { readonly type: \"boolean\"; readonly array: true; readonly required: false; }; readonly \"kibana.alert.instance.id\": { readonly type: \"keyword\"; readonly array: false; readonly required: true; }; readonly \"kibana.alert.last_detected\": { readonly type: \"date\"; readonly required: false; readonly array: false; }; readonly \"kibana.alert.reason\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.category\": { readonly type: \"keyword\"; readonly array: false; readonly required: true; }; readonly \"kibana.alert.rule.consumer\": { readonly type: \"keyword\"; readonly array: false; readonly required: true; }; readonly \"kibana.alert.rule.execution.uuid\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.name\": { readonly type: \"keyword\"; readonly array: false; readonly required: true; }; readonly \"kibana.alert.rule.parameters\": { readonly array: false; readonly type: \"flattened\"; readonly ignore_above: 4096; readonly required: false; }; readonly \"kibana.alert.rule.producer\": { readonly type: \"keyword\"; readonly array: false; readonly required: true; }; readonly \"kibana.alert.rule.tags\": { readonly type: \"keyword\"; readonly array: true; readonly required: false; }; readonly \"kibana.alert.rule.rule_type_id\": { readonly type: \"keyword\"; readonly array: false; readonly required: true; }; readonly \"kibana.alert.rule.uuid\": { readonly type: \"keyword\"; readonly array: false; readonly required: true; }; readonly \"kibana.alert.start\": { readonly type: \"date\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.status\": { readonly type: \"keyword\"; readonly array: false; readonly required: true; }; readonly \"kibana.alert.time_range\": { readonly type: \"date_range\"; readonly format: \"epoch_millis||strict_date_optional_time\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.uuid\": { readonly type: \"keyword\"; readonly array: false; readonly required: true; }; readonly \"kibana.alert.workflow_status\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.space_ids\": { readonly type: \"keyword\"; readonly array: true; readonly required: true; }; readonly \"@timestamp\": { readonly type: \"date\"; readonly required: true; readonly array: false; }; readonly \"kibana.version\": { readonly type: \"version\"; readonly array: false; readonly required: false; }; }" + ], + "path": "packages/kbn-alerts-as-data-utils/src/field_maps/alert_field_map.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/alerts-as-data-utils", + "id": "def-common.ecsFieldMap", + "type": "Object", + "tags": [], + "label": "ecsFieldMap", + "description": [], + "signature": [ + { + "pluginId": "@kbn/alerts-as-data-utils", + "scope": "common", + "docId": "kibKbnAlertsAsDataUtilsPluginApi", + "section": "def-common.FieldMap", + "text": "FieldMap" + } + ], + "path": "packages/kbn-alerts-as-data-utils/src/field_maps/ecs_field_map.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/alerts-as-data-utils", + "id": "def-common.legacyAlertFieldMap", + "type": "Object", + "tags": [], + "label": "legacyAlertFieldMap", + "description": [], + "signature": [ + "{ readonly \"kibana.alert.risk_score\": { readonly type: \"float\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.author\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.created_at\": { readonly type: \"date\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.created_by\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.description\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.enabled\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.from\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.interval\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.license\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.note\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.references\": { readonly type: \"keyword\"; readonly array: true; readonly required: false; }; readonly \"kibana.alert.rule.rule_id\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.rule_name_override\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.to\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.type\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.updated_at\": { readonly type: \"date\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.updated_by\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.rule.version\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.severity\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.suppression.docs_count\": { readonly type: \"long\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.suppression.end\": { readonly type: \"date\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.suppression.terms.field\": { readonly type: \"keyword\"; readonly array: true; readonly required: false; }; readonly \"kibana.alert.suppression.start\": { readonly type: \"date\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.suppression.terms.value\": { readonly type: \"keyword\"; readonly array: true; readonly required: false; }; readonly \"kibana.alert.system_status\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.workflow_reason\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"kibana.alert.workflow_user\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"ecs.version\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"event.action\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly \"event.kind\": { readonly type: \"keyword\"; readonly array: false; readonly required: false; }; readonly tags: { readonly type: \"keyword\"; readonly array: true; readonly required: false; }; }" + ], + "path": "packages/kbn-alerts-as-data-utils/src/field_maps/legacy_alert_field_map.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ] + } +} \ No newline at end of file diff --git a/api_docs/kbn_alerts_as_data_utils.mdx b/api_docs/kbn_alerts_as_data_utils.mdx new file mode 100644 index 0000000000000..992e51d5ca374 --- /dev/null +++ b/api_docs/kbn_alerts_as_data_utils.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: kibKbnAlertsAsDataUtilsPluginApi +slug: /kibana-dev-docs/api/kbn-alerts-as-data-utils +title: "@kbn/alerts-as-data-utils" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/alerts-as-data-utils plugin +date: 2023-02-28 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-as-data-utils'] +--- +import kbnAlertsAsDataUtilsObj from './kbn_alerts_as_data_utils.devdocs.json'; + + + +Contact [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 12 | 0 | 12 | 0 | + +## Common + +### Objects + + +### Interfaces + + +### Consts, variables and types + + diff --git a/api_docs/kbn_alerts_ui_shared.mdx b/api_docs/kbn_alerts_ui_shared.mdx index 629b8977f9f50..0182fb114a71d 100644 --- a/api_docs/kbn_alerts_ui_shared.mdx +++ b/api_docs/kbn_alerts_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-ui-shared title: "@kbn/alerts-ui-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-ui-shared plugin -date: 2023-02-23 +date: 2023-02-28 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 d91e86bd93fff..9684fb67be844 100644 --- a/api_docs/kbn_analytics.mdx +++ b/api_docs/kbn_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics title: "@kbn/analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx index 3fb496d8e4d77..1ba2bd42b6394 100644 --- a/api_docs/kbn_analytics_client.mdx +++ b/api_docs/kbn_analytics_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-client title: "@kbn/analytics-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-client plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-client'] --- import kbnAnalyticsClientObj from './kbn_analytics_client.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx index 8d0c1f90313b3..6d48a60192c3c 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-browser title: "@kbn/analytics-shippers-elastic-v3-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-browser plugin -date: 2023-02-23 +date: 2023-02-28 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 2ae4075f4273f..b4a362286f94b 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-common title: "@kbn/analytics-shippers-elastic-v3-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-common plugin -date: 2023-02-23 +date: 2023-02-28 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 a6a873a4032f8..6cc59522d9f39 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-server title: "@kbn/analytics-shippers-elastic-v3-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-server plugin -date: 2023-02-23 +date: 2023-02-28 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 f1a76a8531a8e..004b9f13a4dee 100644 --- a/api_docs/kbn_analytics_shippers_fullstory.mdx +++ b/api_docs/kbn_analytics_shippers_fullstory.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-fullstory title: "@kbn/analytics-shippers-fullstory" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-fullstory plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-fullstory'] --- import kbnAnalyticsShippersFullstoryObj from './kbn_analytics_shippers_fullstory.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_gainsight.mdx b/api_docs/kbn_analytics_shippers_gainsight.mdx index 6c20d0443c614..a0ab0a9f8c901 100644 --- a/api_docs/kbn_analytics_shippers_gainsight.mdx +++ b/api_docs/kbn_analytics_shippers_gainsight.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-gainsight title: "@kbn/analytics-shippers-gainsight" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-gainsight plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-gainsight'] --- import kbnAnalyticsShippersGainsightObj from './kbn_analytics_shippers_gainsight.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index ce187479cc7c2..10fe54398cb26 100644 --- a/api_docs/kbn_apm_config_loader.mdx +++ b/api_docs/kbn_apm_config_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-config-loader title: "@kbn/apm-config-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-config-loader plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-config-loader'] --- import kbnApmConfigLoaderObj from './kbn_apm_config_loader.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace.mdx b/api_docs/kbn_apm_synthtrace.mdx index b2bae5ef3f2a7..2513fff6c64fd 100644 --- a/api_docs/kbn_apm_synthtrace.mdx +++ b/api_docs/kbn_apm_synthtrace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace title: "@kbn/apm-synthtrace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace plugin -date: 2023-02-23 +date: 2023-02-28 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 ea59a115a467f..ff0b1e2cf64e5 100644 --- a/api_docs/kbn_apm_synthtrace_client.devdocs.json +++ b/api_docs/kbn_apm_synthtrace_client.devdocs.json @@ -2299,7 +2299,7 @@ "section": "def-common.ApmFields", "text": "ApmFields" }, - ", \"@timestamp\" | \"metricset.name\" | \"ecs.version\" | \"event.ingested\" | \"observer.type\" | \"observer.version\" | \"observer.version_major\" | \"processor.event\" | \"processor.name\"> & Partial<{ 'labels.etag': string; agent_config_applied: number; 'event.agent_id_status': string; }>" + ", \"@timestamp\" | \"metricset.name\" | \"ecs.version\" | \"event.ingested\" | \"observer.type\" | \"observer.version_major\" | \"observer.version\" | \"processor.event\" | \"processor.name\"> & Partial<{ 'labels.etag': string; agent_config_applied: number; 'event.agent_id_status': string; }>" ], "path": "packages/kbn-apm-synthtrace-client/src/lib/agent_config/agent_config_fields.ts", "deprecated": false, diff --git a/api_docs/kbn_apm_synthtrace_client.mdx b/api_docs/kbn_apm_synthtrace_client.mdx index 0bdb60fa30dfa..72c107abb92cb 100644 --- a/api_docs/kbn_apm_synthtrace_client.mdx +++ b/api_docs/kbn_apm_synthtrace_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace-client title: "@kbn/apm-synthtrace-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace-client plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace-client'] --- import kbnApmSynthtraceClientObj from './kbn_apm_synthtrace_client.devdocs.json'; diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index b18dc09ab7cb0..49e481e6ae3f9 100644 --- a/api_docs/kbn_apm_utils.mdx +++ b/api_docs/kbn_apm_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-utils title: "@kbn/apm-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-utils plugin -date: 2023-02-23 +date: 2023-02-28 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 14b2ee1330fef..368f913d5c27e 100644 --- a/api_docs/kbn_axe_config.mdx +++ b/api_docs/kbn_axe_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-axe-config title: "@kbn/axe-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/axe-config plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_cases_components.mdx b/api_docs/kbn_cases_components.mdx index 563eaa0044664..c74cd3217452b 100644 --- a/api_docs/kbn_cases_components.mdx +++ b/api_docs/kbn_cases_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cases-components title: "@kbn/cases-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cases-components plugin -date: 2023-02-23 +date: 2023-02-28 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 3df97cc396acf..c793d1c5a5c10 100644 --- a/api_docs/kbn_cell_actions.mdx +++ b/api_docs/kbn_cell_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cell-actions title: "@kbn/cell-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cell-actions plugin -date: 2023-02-23 +date: 2023-02-28 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 537fab223c4cd..4daaf41d72959 100644 --- a/api_docs/kbn_chart_expressions_common.mdx +++ b/api_docs/kbn_chart_expressions_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-expressions-common title: "@kbn/chart-expressions-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-expressions-common plugin -date: 2023-02-23 +date: 2023-02-28 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 4dc0cc964f254..dccd9a9031be5 100644 --- a/api_docs/kbn_chart_icons.mdx +++ b/api_docs/kbn_chart_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-icons title: "@kbn/chart-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-icons plugin -date: 2023-02-23 +date: 2023-02-28 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 ab98555bdf1e1..eab94ac6595e4 100644 --- a/api_docs/kbn_ci_stats_core.mdx +++ b/api_docs/kbn_ci_stats_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-core title: "@kbn/ci-stats-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-core plugin -date: 2023-02-23 +date: 2023-02-28 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 99a3f43186b33..07ae40b17946c 100644 --- a/api_docs/kbn_ci_stats_performance_metrics.mdx +++ b/api_docs/kbn_ci_stats_performance_metrics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-performance-metrics title: "@kbn/ci-stats-performance-metrics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-performance-metrics plugin -date: 2023-02-23 +date: 2023-02-28 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 9f1614662a44d..73a06511196e7 100644 --- a/api_docs/kbn_ci_stats_reporter.mdx +++ b/api_docs/kbn_ci_stats_reporter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-reporter title: "@kbn/ci-stats-reporter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-reporter plugin -date: 2023-02-23 +date: 2023-02-28 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 d80b8fc36ce3f..3787f059786a8 100644 --- a/api_docs/kbn_cli_dev_mode.mdx +++ b/api_docs/kbn_cli_dev_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cli-dev-mode title: "@kbn/cli-dev-mode" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cli-dev-mode plugin -date: 2023-02-23 +date: 2023-02-28 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 839ac6b8b82f0..2178f170966fc 100644 --- a/api_docs/kbn_code_editor.mdx +++ b/api_docs/kbn_code_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor title: "@kbn/code-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor'] --- import kbnCodeEditorObj from './kbn_code_editor.devdocs.json'; diff --git a/api_docs/kbn_code_editor_mocks.mdx b/api_docs/kbn_code_editor_mocks.mdx index 538e631eac44d..09a047af41856 100644 --- a/api_docs/kbn_code_editor_mocks.mdx +++ b/api_docs/kbn_code_editor_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor-mocks title: "@kbn/code-editor-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor-mocks plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor-mocks'] --- import kbnCodeEditorMocksObj from './kbn_code_editor_mocks.devdocs.json'; diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index 519c60ae61756..620e35d139390 100644 --- a/api_docs/kbn_coloring.mdx +++ b/api_docs/kbn_coloring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-coloring title: "@kbn/coloring" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/coloring plugin -date: 2023-02-23 +date: 2023-02-28 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 24c50a021dc37..e28a6f5f0959e 100644 --- a/api_docs/kbn_config.mdx +++ b/api_docs/kbn_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config title: "@kbn/config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config plugin -date: 2023-02-23 +date: 2023-02-28 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 6dc08a9cd9614..d3c4cd303f272 100644 --- a/api_docs/kbn_config_mocks.mdx +++ b/api_docs/kbn_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-mocks title: "@kbn/config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 79badc21baa2a..7d646114fb442 100644 --- a/api_docs/kbn_config_schema.mdx +++ b/api_docs/kbn_config_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-schema title: "@kbn/config-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-schema plugin -date: 2023-02-23 +date: 2023-02-28 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 ac0b7d94be101..2172cd92ee114 100644 --- a/api_docs/kbn_content_management_content_editor.mdx +++ b/api_docs/kbn_content_management_content_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-content-editor title: "@kbn/content-management-content-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-content-editor plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-content-editor'] --- import kbnContentManagementContentEditorObj from './kbn_content_management_content_editor.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list.mdx b/api_docs/kbn_content_management_table_list.mdx index a6ba95781582f..a7f6b6fb80369 100644 --- a/api_docs/kbn_content_management_table_list.mdx +++ b/api_docs/kbn_content_management_table_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list title: "@kbn/content-management-table-list" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list'] --- import kbnContentManagementTableListObj from './kbn_content_management_table_list.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index beb2e696b4f23..fe9deb4b585ac 100644 --- a/api_docs/kbn_core_analytics_browser.mdx +++ b/api_docs/kbn_core_analytics_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser title: "@kbn/core-analytics-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser plugin -date: 2023-02-23 +date: 2023-02-28 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 3164b80620f2b..38fb24b930ca7 100644 --- a/api_docs/kbn_core_analytics_browser_internal.mdx +++ b/api_docs/kbn_core_analytics_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-internal title: "@kbn/core-analytics-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 3cbec044afc3e..432774cbff830 100644 --- a/api_docs/kbn_core_analytics_browser_mocks.mdx +++ b/api_docs/kbn_core_analytics_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-mocks title: "@kbn/core-analytics-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 9f3dbb2b3601a..9c3b01c84d7de 100644 --- a/api_docs/kbn_core_analytics_server.mdx +++ b/api_docs/kbn_core_analytics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server title: "@kbn/core-analytics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server plugin -date: 2023-02-23 +date: 2023-02-28 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 4d5995b7e1428..c142aabd9a321 100644 --- a/api_docs/kbn_core_analytics_server_internal.mdx +++ b/api_docs/kbn_core_analytics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-internal title: "@kbn/core-analytics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 3200c12824129..f868c799f5928 100644 --- a/api_docs/kbn_core_analytics_server_mocks.mdx +++ b/api_docs/kbn_core_analytics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-mocks title: "@kbn/core-analytics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 0cfc4cbe36ccd..6f8e2526177fa 100644 --- a/api_docs/kbn_core_application_browser.mdx +++ b/api_docs/kbn_core_application_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser title: "@kbn/core-application-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser plugin -date: 2023-02-23 +date: 2023-02-28 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 8c1300d888b8c..8f4d90f1df324 100644 --- a/api_docs/kbn_core_application_browser_internal.mdx +++ b/api_docs/kbn_core_application_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-internal title: "@kbn/core-application-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 8b1488084e99e..b0c74a42b25ac 100644 --- a/api_docs/kbn_core_application_browser_mocks.mdx +++ b/api_docs/kbn_core_application_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-mocks title: "@kbn/core-application-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 61284a273c143..f194c2065f746 100644 --- a/api_docs/kbn_core_application_common.mdx +++ b/api_docs/kbn_core_application_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-common title: "@kbn/core-application-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-common plugin -date: 2023-02-23 +date: 2023-02-28 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 ae139a215a014..7ddaa26562b22 100644 --- a/api_docs/kbn_core_apps_browser_internal.mdx +++ b/api_docs/kbn_core_apps_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-internal title: "@kbn/core-apps-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 76f09b08ed6a9..27b03ed3a1a21 100644 --- a/api_docs/kbn_core_apps_browser_mocks.mdx +++ b/api_docs/kbn_core_apps_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-mocks title: "@kbn/core-apps-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 bc333621d3185..aa9b5e329e9e8 100644 --- a/api_docs/kbn_core_apps_server_internal.mdx +++ b/api_docs/kbn_core_apps_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-server-internal title: "@kbn/core-apps-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-server-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 340d23dc22a15..26dbd86f7ff30 100644 --- a/api_docs/kbn_core_base_browser_mocks.mdx +++ b/api_docs/kbn_core_base_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-browser-mocks title: "@kbn/core-base-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-browser-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 cda9e5a2d4753..50a40e42e8f38 100644 --- a/api_docs/kbn_core_base_common.mdx +++ b/api_docs/kbn_core_base_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-common title: "@kbn/core-base-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-common plugin -date: 2023-02-23 +date: 2023-02-28 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 8e1a65a454b58..b8f1c44efe947 100644 --- a/api_docs/kbn_core_base_server_internal.mdx +++ b/api_docs/kbn_core_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-internal title: "@kbn/core-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 d9d3536fdb9fa..2ebce3e9b4460 100644 --- a/api_docs/kbn_core_base_server_mocks.mdx +++ b/api_docs/kbn_core_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-mocks title: "@kbn/core-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 789bb35001a4a..0093670745c9d 100644 --- a/api_docs/kbn_core_capabilities_browser_mocks.mdx +++ b/api_docs/kbn_core_capabilities_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-browser-mocks title: "@kbn/core-capabilities-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-browser-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 c33cb8e2a18e5..2d97c57d5a937 100644 --- a/api_docs/kbn_core_capabilities_common.mdx +++ b/api_docs/kbn_core_capabilities_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-common title: "@kbn/core-capabilities-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-common plugin -date: 2023-02-23 +date: 2023-02-28 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 59e3a4e907760..f6998d9b6f6bd 100644 --- a/api_docs/kbn_core_capabilities_server.mdx +++ b/api_docs/kbn_core_capabilities_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server title: "@kbn/core-capabilities-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server plugin -date: 2023-02-23 +date: 2023-02-28 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 910aa1a5c7d84..a5164a238b129 100644 --- a/api_docs/kbn_core_capabilities_server_mocks.mdx +++ b/api_docs/kbn_core_capabilities_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server-mocks title: "@kbn/core-capabilities-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 8deac5848d987..40a0b78ba18ea 100644 --- a/api_docs/kbn_core_chrome_browser.mdx +++ b/api_docs/kbn_core_chrome_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser title: "@kbn/core-chrome-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser plugin -date: 2023-02-23 +date: 2023-02-28 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 50144ffd9d797..9adcd8599792f 100644 --- a/api_docs/kbn_core_chrome_browser_mocks.mdx +++ b/api_docs/kbn_core_chrome_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser-mocks title: "@kbn/core-chrome-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 aeebf860f6b99..3c464b4c79406 100644 --- a/api_docs/kbn_core_config_server_internal.mdx +++ b/api_docs/kbn_core_config_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-config-server-internal title: "@kbn/core-config-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-config-server-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 9bd441e7092a6..3065855251657 100644 --- a/api_docs/kbn_core_custom_branding_browser.mdx +++ b/api_docs/kbn_core_custom_branding_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser title: "@kbn/core-custom-branding-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser plugin -date: 2023-02-23 +date: 2023-02-28 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 cd3bf482e31a0..8ec8f5a3543ce 100644 --- a/api_docs/kbn_core_custom_branding_browser_internal.mdx +++ b/api_docs/kbn_core_custom_branding_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-internal title: "@kbn/core-custom-branding-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 e9dd317feaf01..347beed689407 100644 --- a/api_docs/kbn_core_custom_branding_browser_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-mocks title: "@kbn/core-custom-branding-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 357232b011e29..85b0225e69aea 100644 --- a/api_docs/kbn_core_custom_branding_common.mdx +++ b/api_docs/kbn_core_custom_branding_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-common title: "@kbn/core-custom-branding-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-common plugin -date: 2023-02-23 +date: 2023-02-28 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 dcc080bcb271e..6f6646157d997 100644 --- a/api_docs/kbn_core_custom_branding_server.mdx +++ b/api_docs/kbn_core_custom_branding_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server title: "@kbn/core-custom-branding-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server plugin -date: 2023-02-23 +date: 2023-02-28 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 0176d2d802afd..f94be732c56bb 100644 --- a/api_docs/kbn_core_custom_branding_server_internal.mdx +++ b/api_docs/kbn_core_custom_branding_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-internal title: "@kbn/core-custom-branding-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 bcdc5789ee17c..1e6dddce80d5a 100644 --- a/api_docs/kbn_core_custom_branding_server_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-mocks title: "@kbn/core-custom-branding-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 a12c39818bf56..04072fdc8bbfe 100644 --- a/api_docs/kbn_core_deprecations_browser.mdx +++ b/api_docs/kbn_core_deprecations_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser title: "@kbn/core-deprecations-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser plugin -date: 2023-02-23 +date: 2023-02-28 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 ac7b6053939d3..95222a6ac1824 100644 --- a/api_docs/kbn_core_deprecations_browser_internal.mdx +++ b/api_docs/kbn_core_deprecations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-internal title: "@kbn/core-deprecations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 d3f5f74c873aa..0f1e4720014ac 100644 --- a/api_docs/kbn_core_deprecations_browser_mocks.mdx +++ b/api_docs/kbn_core_deprecations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-mocks title: "@kbn/core-deprecations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 adb396b6d197c..c497ffb4366d2 100644 --- a/api_docs/kbn_core_deprecations_common.mdx +++ b/api_docs/kbn_core_deprecations_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-common title: "@kbn/core-deprecations-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-common plugin -date: 2023-02-23 +date: 2023-02-28 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 fa5e84b4d70d6..fb218b43f4269 100644 --- a/api_docs/kbn_core_deprecations_server.mdx +++ b/api_docs/kbn_core_deprecations_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server title: "@kbn/core-deprecations-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server plugin -date: 2023-02-23 +date: 2023-02-28 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 6adacd869ce6b..0d095d6f7c53d 100644 --- a/api_docs/kbn_core_deprecations_server_internal.mdx +++ b/api_docs/kbn_core_deprecations_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-internal title: "@kbn/core-deprecations-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 e327246b03b84..e2ea45eb55375 100644 --- a/api_docs/kbn_core_deprecations_server_mocks.mdx +++ b/api_docs/kbn_core_deprecations_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-mocks title: "@kbn/core-deprecations-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 b824f11b12cc4..bb0d0cc31bead 100644 --- a/api_docs/kbn_core_doc_links_browser.mdx +++ b/api_docs/kbn_core_doc_links_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser title: "@kbn/core-doc-links-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser plugin -date: 2023-02-23 +date: 2023-02-28 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 3d14d324606e5..2cee9209cdbef 100644 --- a/api_docs/kbn_core_doc_links_browser_mocks.mdx +++ b/api_docs/kbn_core_doc_links_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser-mocks title: "@kbn/core-doc-links-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 9d09bc1e4eddc..7a30a0bed12ef 100644 --- a/api_docs/kbn_core_doc_links_server.mdx +++ b/api_docs/kbn_core_doc_links_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server title: "@kbn/core-doc-links-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server plugin -date: 2023-02-23 +date: 2023-02-28 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 94813651787a3..b815a71f4dea4 100644 --- a/api_docs/kbn_core_doc_links_server_mocks.mdx +++ b/api_docs/kbn_core_doc_links_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server-mocks title: "@kbn/core-doc-links-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 473cac0b080ae..24e0983a06988 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-internal title: "@kbn/core-elasticsearch-client-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 f25726a77ac1b..8940205ac39e7 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-mocks title: "@kbn/core-elasticsearch-client-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 9a79ed8dc31bc..09cf5a04b533b 100644 --- a/api_docs/kbn_core_elasticsearch_server.mdx +++ b/api_docs/kbn_core_elasticsearch_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server title: "@kbn/core-elasticsearch-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server'] --- import kbnCoreElasticsearchServerObj from './kbn_core_elasticsearch_server.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server_internal.mdx b/api_docs/kbn_core_elasticsearch_server_internal.mdx index 27d04e7e4c0b5..a89837fea7fa1 100644 --- a/api_docs/kbn_core_elasticsearch_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-internal title: "@kbn/core-elasticsearch-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 ce9b9ae358c49..32bca8361a9f2 100644 --- a/api_docs/kbn_core_elasticsearch_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-mocks title: "@kbn/core-elasticsearch-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 9227ca8eaec89..dd94662c3b9a4 100644 --- a/api_docs/kbn_core_environment_server_internal.mdx +++ b/api_docs/kbn_core_environment_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-internal title: "@kbn/core-environment-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 fed7beb884a54..c2260e5150dd2 100644 --- a/api_docs/kbn_core_environment_server_mocks.mdx +++ b/api_docs/kbn_core_environment_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-mocks title: "@kbn/core-environment-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 d5cf83976adde..5035a999507ef 100644 --- a/api_docs/kbn_core_execution_context_browser.mdx +++ b/api_docs/kbn_core_execution_context_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser title: "@kbn/core-execution-context-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser plugin -date: 2023-02-23 +date: 2023-02-28 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 85fbc49e94c44..32c3f93a2147d 100644 --- a/api_docs/kbn_core_execution_context_browser_internal.mdx +++ b/api_docs/kbn_core_execution_context_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-internal title: "@kbn/core-execution-context-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 e217e9e4ce68a..9e817bea42692 100644 --- a/api_docs/kbn_core_execution_context_browser_mocks.mdx +++ b/api_docs/kbn_core_execution_context_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-mocks title: "@kbn/core-execution-context-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 ccbf8a15eca28..38992012702e3 100644 --- a/api_docs/kbn_core_execution_context_common.mdx +++ b/api_docs/kbn_core_execution_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-common title: "@kbn/core-execution-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-common plugin -date: 2023-02-23 +date: 2023-02-28 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 d6ed7526a7601..997277de0992c 100644 --- a/api_docs/kbn_core_execution_context_server.mdx +++ b/api_docs/kbn_core_execution_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server title: "@kbn/core-execution-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server plugin -date: 2023-02-23 +date: 2023-02-28 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 ac64f7e0dbbee..590a32f7b6804 100644 --- a/api_docs/kbn_core_execution_context_server_internal.mdx +++ b/api_docs/kbn_core_execution_context_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-internal title: "@kbn/core-execution-context-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 9619392e6f5bd..0a2e946d0ef46 100644 --- a/api_docs/kbn_core_execution_context_server_mocks.mdx +++ b/api_docs/kbn_core_execution_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-mocks title: "@kbn/core-execution-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 929e3d19f81ff..6b4a94c7a65c0 100644 --- a/api_docs/kbn_core_fatal_errors_browser.mdx +++ b/api_docs/kbn_core_fatal_errors_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser title: "@kbn/core-fatal-errors-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser plugin -date: 2023-02-23 +date: 2023-02-28 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 c41189565d94b..9a009a36d14b0 100644 --- a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx +++ b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser-mocks title: "@kbn/core-fatal-errors-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 1042d66d77789..b71faf9a8d6ce 100644 --- a/api_docs/kbn_core_http_browser.mdx +++ b/api_docs/kbn_core_http_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser title: "@kbn/core-http-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser plugin -date: 2023-02-23 +date: 2023-02-28 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 9c31fe9c6638e..dcbda996bb75e 100644 --- a/api_docs/kbn_core_http_browser_internal.mdx +++ b/api_docs/kbn_core_http_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-internal title: "@kbn/core-http-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 3befb77285a50..62c46d3b36627 100644 --- a/api_docs/kbn_core_http_browser_mocks.mdx +++ b/api_docs/kbn_core_http_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-mocks title: "@kbn/core-http-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 60adeb7106da3..198ef483d2837 100644 --- a/api_docs/kbn_core_http_common.mdx +++ b/api_docs/kbn_core_http_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-common title: "@kbn/core-http-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-common plugin -date: 2023-02-23 +date: 2023-02-28 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 83411d41521fe..7f339abb6cb8a 100644 --- a/api_docs/kbn_core_http_context_server_mocks.mdx +++ b/api_docs/kbn_core_http_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-context-server-mocks title: "@kbn/core-http-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-context-server-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 134f049312fd7..8c04ac064bebe 100644 --- a/api_docs/kbn_core_http_request_handler_context_server.mdx +++ b/api_docs/kbn_core_http_request_handler_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-request-handler-context-server title: "@kbn/core-http-request-handler-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-request-handler-context-server plugin -date: 2023-02-23 +date: 2023-02-28 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.devdocs.json b/api_docs/kbn_core_http_resources_server.devdocs.json index ac3aa3ad57cf8..a8899047410a4 100644 --- a/api_docs/kbn_core_http_resources_server.devdocs.json +++ b/api_docs/kbn_core_http_resources_server.devdocs.json @@ -513,7 +513,33 @@ "section": "def-common.KibanaErrorResponseFactory", "text": "KibanaErrorResponseFactory" }, - " & { custom | Error | Buffer | ", + " & { file | Error | Buffer | ", + "Stream", + " | { message: string | Error; attributes?: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.ResponseErrorAttributes", + "text": "ResponseErrorAttributes" + }, + " | undefined; } | undefined>(options: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.FileHttpResponseOptions", + "text": "FileHttpResponseOptions" + }, + "): ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.IKibanaResponse", + "text": "IKibanaResponse" + }, + "; custom | Error | Buffer | ", "Stream", " | { message: string | Error; attributes?: ", { diff --git a/api_docs/kbn_core_http_resources_server.mdx b/api_docs/kbn_core_http_resources_server.mdx index 5bff294a82274..82dea39db37f3 100644 --- a/api_docs/kbn_core_http_resources_server.mdx +++ b/api_docs/kbn_core_http_resources_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server title: "@kbn/core-http-resources-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server plugin -date: 2023-02-23 +date: 2023-02-28 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 78ebb26d8d35d..c7f2baee7037c 100644 --- a/api_docs/kbn_core_http_resources_server_internal.mdx +++ b/api_docs/kbn_core_http_resources_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-internal title: "@kbn/core-http-resources-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-internal plugin -date: 2023-02-23 +date: 2023-02-28 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.devdocs.json b/api_docs/kbn_core_http_resources_server_mocks.devdocs.json index c8bdc7c3f7473..12acbace23682 100644 --- a/api_docs/kbn_core_http_resources_server_mocks.devdocs.json +++ b/api_docs/kbn_core_http_resources_server_mocks.devdocs.json @@ -721,6 +721,58 @@ "section": "def-common.IKibanaResponse", "text": "IKibanaResponse" }, + "); file: jest.MockInstance<", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.IKibanaResponse", + "text": "IKibanaResponse" + }, + ", [options: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.FileHttpResponseOptions", + "text": "FileHttpResponseOptions" + }, + " | Error | Buffer | ", + "Stream", + " | { message: string | Error; attributes?: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.ResponseErrorAttributes", + "text": "ResponseErrorAttributes" + }, + " | undefined; } | undefined>]> & ( | Error | Buffer | ", + "Stream", + " | { message: string | Error; attributes?: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.ResponseErrorAttributes", + "text": "ResponseErrorAttributes" + }, + " | undefined; } | undefined>(options: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.FileHttpResponseOptions", + "text": "FileHttpResponseOptions" + }, + ") => ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.IKibanaResponse", + "text": "IKibanaResponse" + }, "); custom: jest.MockInstance<", { "pluginId": "@kbn/core-http-server", diff --git a/api_docs/kbn_core_http_resources_server_mocks.mdx b/api_docs/kbn_core_http_resources_server_mocks.mdx index 4bdf848cd7563..5c962b28d9886 100644 --- a/api_docs/kbn_core_http_resources_server_mocks.mdx +++ b/api_docs/kbn_core_http_resources_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-mocks title: "@kbn/core-http-resources-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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.devdocs.json b/api_docs/kbn_core_http_router_server_internal.devdocs.json index ee67394ec6432..f3b8701d3798e 100644 --- a/api_docs/kbn_core_http_router_server_internal.devdocs.json +++ b/api_docs/kbn_core_http_router_server_internal.devdocs.json @@ -379,6 +379,20 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "@kbn/core-http-router-server-internal", + "id": "def-common.kibanaResponseFactory.Unnamed", + "type": "Any", + "tags": [], + "label": "Unnamed", + "description": [], + "signature": [ + "any" + ], + "path": "packages/core/http/core-http-router-server-internal/src/response.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "@kbn/core-http-router-server-internal", "id": "def-common.kibanaResponseFactory.custom", diff --git a/api_docs/kbn_core_http_router_server_internal.mdx b/api_docs/kbn_core_http_router_server_internal.mdx index 7504850928b60..cce44307af439 100644 --- a/api_docs/kbn_core_http_router_server_internal.mdx +++ b/api_docs/kbn_core_http_router_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-internal title: "@kbn/core-http-router-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-internal plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-internal'] --- import kbnCoreHttpRouterServerInternalObj from './kbn_core_http_router_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 | |-------------------|-----------|------------------------|-----------------| -| 25 | 5 | 25 | 1 | +| 26 | 6 | 26 | 1 | ## Common diff --git a/api_docs/kbn_core_http_router_server_mocks.mdx b/api_docs/kbn_core_http_router_server_mocks.mdx index ae9baea3c7c03..b7547349d8917 100644 --- a/api_docs/kbn_core_http_router_server_mocks.mdx +++ b/api_docs/kbn_core_http_router_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-mocks title: "@kbn/core-http-router-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 35891c964b6d2..24328cd2e9a6b 100644 --- a/api_docs/kbn_core_http_server.devdocs.json +++ b/api_docs/kbn_core_http_server.devdocs.json @@ -788,6 +788,148 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/core-http-server", + "id": "def-common.FileHttpResponseOptions", + "type": "Interface", + "tags": [], + "label": "FileHttpResponseOptions", + "description": [ + "\nHTTP response parameters for a response with adjustable status code." + ], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.FileHttpResponseOptions", + "text": "FileHttpResponseOptions" + }, + "" + ], + "path": "packages/core/http/core-http-server/src/router/response.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-server", + "id": "def-common.FileHttpResponseOptions.body", + "type": "Uncategorized", + "tags": [], + "label": "body", + "description": [ + "Attachment content to send to the client" + ], + "signature": [ + "T" + ], + "path": "packages/core/http/core-http-server/src/router/response.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-http-server", + "id": "def-common.FileHttpResponseOptions.filename", + "type": "string", + "tags": [], + "label": "filename", + "description": [ + "Attachment name, encoded and added to the headers to send to the client" + ], + "path": "packages/core/http/core-http-server/src/router/response.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-http-server", + "id": "def-common.FileHttpResponseOptions.fileContentType", + "type": "CompoundType", + "tags": [], + "label": "fileContentType", + "description": [ + "Explicitly set the attachment content type. Tries to detect the type based on extension and defaults to application/octet-stream" + ], + "signature": [ + "string | null | undefined" + ], + "path": "packages/core/http/core-http-server/src/router/response.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-http-server", + "id": "def-common.FileHttpResponseOptions.fileContentSize", + "type": "number", + "tags": [], + "label": "fileContentSize", + "description": [ + "Attachment content size in bytes, Tries to detect the content size from body" + ], + "signature": [ + "number | undefined" + ], + "path": "packages/core/http/core-http-server/src/router/response.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-http-server", + "id": "def-common.FileHttpResponseOptions.headers", + "type": "CompoundType", + "tags": [], + "label": "headers", + "description": [ + "HTTP Headers with additional information about response" + ], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.ResponseHeaders", + "text": "ResponseHeaders" + }, + " | undefined" + ], + "path": "packages/core/http/core-http-server/src/router/response.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-http-server", + "id": "def-common.FileHttpResponseOptions.bypassErrorFormat", + "type": "CompoundType", + "tags": [], + "label": "bypassErrorFormat", + "description": [ + "Bypass the default error formatting" + ], + "signature": [ + "boolean | undefined" + ], + "path": "packages/core/http/core-http-server/src/router/response.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-http-server", + "id": "def-common.FileHttpResponseOptions.bypassFileNameEncoding", + "type": "CompoundType", + "tags": [], + "label": "bypassFileNameEncoding", + "description": [ + "Bypass filename encoding, only set to true if the filename is already encoded" + ], + "signature": [ + "boolean | undefined" + ], + "path": "packages/core/http/core-http-server/src/router/response.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/core-http-server", "id": "def-common.HttpAuth", @@ -7520,7 +7662,33 @@ "section": "def-common.KibanaErrorResponseFactory", "text": "KibanaErrorResponseFactory" }, - " & { custom | Error | Buffer | ", + " & { file | Error | Buffer | ", + "Stream", + " | { message: string | Error; attributes?: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.ResponseErrorAttributes", + "text": "ResponseErrorAttributes" + }, + " | undefined; } | undefined>(options: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.FileHttpResponseOptions", + "text": "FileHttpResponseOptions" + }, + "): ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.IKibanaResponse", + "text": "IKibanaResponse" + }, + "; custom | Error | Buffer | ", "Stream", " | { message: string | Error; attributes?: ", { diff --git a/api_docs/kbn_core_http_server.mdx b/api_docs/kbn_core_http_server.mdx index 2db16608390cb..d65a953512a37 100644 --- a/api_docs/kbn_core_http_server.mdx +++ b/api_docs/kbn_core_http_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server title: "@kbn/core-http-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server'] --- import kbnCoreHttpServerObj from './kbn_core_http_server.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 | |-------------------|-----------|------------------------|-----------------| -| 403 | 1 | 160 | 0 | +| 411 | 1 | 160 | 0 | ## Common diff --git a/api_docs/kbn_core_http_server_internal.mdx b/api_docs/kbn_core_http_server_internal.mdx index fd2e91389a3d2..96aadf2d16be1 100644 --- a/api_docs/kbn_core_http_server_internal.mdx +++ b/api_docs/kbn_core_http_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-internal title: "@kbn/core-http-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-internal plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-internal'] --- import kbnCoreHttpServerInternalObj from './kbn_core_http_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_mocks.mdx b/api_docs/kbn_core_http_server_mocks.mdx index 2d0168c3ff4ed..489443cba0e0b 100644 --- a/api_docs/kbn_core_http_server_mocks.mdx +++ b/api_docs/kbn_core_http_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-mocks title: "@kbn/core-http-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 f120794ee1399..f50069c3f3d4a 100644 --- a/api_docs/kbn_core_i18n_browser.mdx +++ b/api_docs/kbn_core_i18n_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser title: "@kbn/core-i18n-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser plugin -date: 2023-02-23 +date: 2023-02-28 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 476e8d58a94d3..d634637079eca 100644 --- a/api_docs/kbn_core_i18n_browser_mocks.mdx +++ b/api_docs/kbn_core_i18n_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser-mocks title: "@kbn/core-i18n-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 1e4d52382a46c..ef3c68b517d8c 100644 --- a/api_docs/kbn_core_i18n_server.mdx +++ b/api_docs/kbn_core_i18n_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server title: "@kbn/core-i18n-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server plugin -date: 2023-02-23 +date: 2023-02-28 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 c432396c69cbb..8b3b165d647b1 100644 --- a/api_docs/kbn_core_i18n_server_internal.mdx +++ b/api_docs/kbn_core_i18n_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-internal title: "@kbn/core-i18n-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 33b7a47c202d3..46b7c178fcc81 100644 --- a/api_docs/kbn_core_i18n_server_mocks.mdx +++ b/api_docs/kbn_core_i18n_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-mocks title: "@kbn/core-i18n-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 2ae998984baef..67f4789cf12a7 100644 --- a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx +++ b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser-mocks title: "@kbn/core-injected-metadata-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-injected-metadata-browser-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 672868b8e95f4..4b3c13625ec7b 100644 --- a/api_docs/kbn_core_integrations_browser_internal.mdx +++ b/api_docs/kbn_core_integrations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-internal title: "@kbn/core-integrations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 7683e5922c1c2..ea11c4da9c361 100644 --- a/api_docs/kbn_core_integrations_browser_mocks.mdx +++ b/api_docs/kbn_core_integrations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-mocks title: "@kbn/core-integrations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 92f057a5d368e..3f09d5bddec53 100644 --- a/api_docs/kbn_core_lifecycle_browser.mdx +++ b/api_docs/kbn_core_lifecycle_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser title: "@kbn/core-lifecycle-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser plugin -date: 2023-02-23 +date: 2023-02-28 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 2331bb685a0da..8c504dfc42433 100644 --- a/api_docs/kbn_core_lifecycle_browser_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser-mocks title: "@kbn/core-lifecycle-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 608ff582b821e..6e1ad00dc2109 100644 --- a/api_docs/kbn_core_lifecycle_server.mdx +++ b/api_docs/kbn_core_lifecycle_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server title: "@kbn/core-lifecycle-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server plugin -date: 2023-02-23 +date: 2023-02-28 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 5c62febfc6c44..8866307025a39 100644 --- a/api_docs/kbn_core_lifecycle_server_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server-mocks title: "@kbn/core-lifecycle-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 b1197bd76cb99..f8f39b499356f 100644 --- a/api_docs/kbn_core_logging_browser_mocks.mdx +++ b/api_docs/kbn_core_logging_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-browser-mocks title: "@kbn/core-logging-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-browser-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 210811465b9a6..546977f0811d0 100644 --- a/api_docs/kbn_core_logging_common_internal.mdx +++ b/api_docs/kbn_core_logging_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-common-internal title: "@kbn/core-logging-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-common-internal plugin -date: 2023-02-23 +date: 2023-02-28 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.devdocs.json b/api_docs/kbn_core_logging_server.devdocs.json index 3341d022e8280..edacae8c2aa44 100644 --- a/api_docs/kbn_core_logging_server.devdocs.json +++ b/api_docs/kbn_core_logging_server.devdocs.json @@ -223,7 +223,7 @@ "label": "level", "description": [], "signature": [ - "\"error\" | \"off\" | \"all\" | \"debug\" | \"info\" | \"warn\" | \"trace\" | \"fatal\"" + "\"error\" | \"all\" | \"off\" | \"debug\" | \"info\" | \"warn\" | \"trace\" | \"fatal\"" ], "path": "packages/core/logging/core-logging-server/src/logger.ts", "deprecated": false, diff --git a/api_docs/kbn_core_logging_server.mdx b/api_docs/kbn_core_logging_server.mdx index b878f016a34b6..0b0c961bc0b74 100644 --- a/api_docs/kbn_core_logging_server.mdx +++ b/api_docs/kbn_core_logging_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server title: "@kbn/core-logging-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server plugin -date: 2023-02-23 +date: 2023-02-28 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.devdocs.json b/api_docs/kbn_core_logging_server_internal.devdocs.json index d304656de7192..fd6f6dc3b8731 100644 --- a/api_docs/kbn_core_logging_server_internal.devdocs.json +++ b/api_docs/kbn_core_logging_server_internal.devdocs.json @@ -199,7 +199,7 @@ "section": "def-common.Type", "text": "Type" }, - "[]>; }>" + "[]>; }>" ], "path": "packages/core/logging/core-logging-server-internal/src/logging_config.ts", "deprecated": false, @@ -247,7 +247,7 @@ "section": "def-common.Type", "text": "Type" }, - "<\"error\" | \"off\" | \"all\" | \"debug\" | \"info\" | \"warn\" | \"trace\" | \"fatal\">; }>" + "<\"error\" | \"all\" | \"off\" | \"debug\" | \"info\" | \"warn\" | \"trace\" | \"fatal\">; }>" ], "path": "packages/core/logging/core-logging-server-internal/src/logging_config.ts", "deprecated": false, diff --git a/api_docs/kbn_core_logging_server_internal.mdx b/api_docs/kbn_core_logging_server_internal.mdx index 3c89cbecdf4da..0e38ee4bee9cb 100644 --- a/api_docs/kbn_core_logging_server_internal.mdx +++ b/api_docs/kbn_core_logging_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-internal title: "@kbn/core-logging-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 f3f5c74f67dfb..477dc6dcfbfae 100644 --- a/api_docs/kbn_core_logging_server_mocks.mdx +++ b/api_docs/kbn_core_logging_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-mocks title: "@kbn/core-logging-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 6d95dc80030c6..16858dd741c08 100644 --- a/api_docs/kbn_core_metrics_collectors_server_internal.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-internal title: "@kbn/core-metrics-collectors-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 fae8462cf629f..cd7556995b5b1 100644 --- a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-mocks title: "@kbn/core-metrics-collectors-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 fdb08240196fd..a1e3c4bcf55e3 100644 --- a/api_docs/kbn_core_metrics_server.mdx +++ b/api_docs/kbn_core_metrics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server title: "@kbn/core-metrics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server plugin -date: 2023-02-23 +date: 2023-02-28 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 0ff31d2724680..b4382c5f92369 100644 --- a/api_docs/kbn_core_metrics_server_internal.mdx +++ b/api_docs/kbn_core_metrics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-internal title: "@kbn/core-metrics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 4ab194f3edf68..5301a90c90b28 100644 --- a/api_docs/kbn_core_metrics_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-mocks title: "@kbn/core-metrics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 2eb64b61065d2..4f6bf040660eb 100644 --- a/api_docs/kbn_core_mount_utils_browser.mdx +++ b/api_docs/kbn_core_mount_utils_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-mount-utils-browser title: "@kbn/core-mount-utils-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-mount-utils-browser plugin -date: 2023-02-23 +date: 2023-02-28 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.devdocs.json b/api_docs/kbn_core_node_server.devdocs.json index 9b0d9eed398dd..92ce832afbc4e 100644 --- a/api_docs/kbn_core_node_server.devdocs.json +++ b/api_docs/kbn_core_node_server.devdocs.json @@ -96,6 +96,19 @@ "path": "packages/core/node/core-node-server/src/types.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-node-server", + "id": "def-common.NodeRoles.migrator", + "type": "boolean", + "tags": [], + "label": "migrator", + "description": [ + "\nStart Kibana with the specific purpose of completing the migrations phase then shutting down." + ], + "path": "packages/core/node/core-node-server/src/types.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/kbn_core_node_server.mdx b/api_docs/kbn_core_node_server.mdx index 1f88c5162b67c..972b3c1597f3c 100644 --- a/api_docs/kbn_core_node_server.mdx +++ b/api_docs/kbn_core_node_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server title: "@kbn/core-node-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server'] --- import kbnCoreNodeServerObj from './kbn_core_node_server.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 | |-------------------|-----------|------------------------|-----------------| -| 5 | 0 | 0 | 0 | +| 6 | 0 | 0 | 0 | ## Common diff --git a/api_docs/kbn_core_node_server_internal.devdocs.json b/api_docs/kbn_core_node_server_internal.devdocs.json index 8db2a68da0239..435988185b457 100644 --- a/api_docs/kbn_core_node_server_internal.devdocs.json +++ b/api_docs/kbn_core_node_server_internal.devdocs.json @@ -87,64 +87,6 @@ ], "enums": [], "misc": [], - "objects": [ - { - "parentPluginId": "@kbn/core-node-server-internal", - "id": "def-common.nodeConfig", - "type": "Object", - "tags": [], - "label": "nodeConfig", - "description": [], - "path": "packages/core/node/core-node-server-internal/src/node_config.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-node-server-internal", - "id": "def-common.nodeConfig.path", - "type": "string", - "tags": [], - "label": "path", - "description": [], - "signature": [ - "\"node\"" - ], - "path": "packages/core/node/core-node-server-internal/src/node_config.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/core-node-server-internal", - "id": "def-common.nodeConfig.schema", - "type": "Object", - "tags": [], - "label": "schema", - "description": [], - "signature": [ - { - "pluginId": "@kbn/config-schema", - "scope": "common", - "docId": "kibKbnConfigSchemaPluginApi", - "section": "def-common.ObjectType", - "text": "ObjectType" - }, - "<{ roles: ", - { - "pluginId": "@kbn/config-schema", - "scope": "common", - "docId": "kibKbnConfigSchemaPluginApi", - "section": "def-common.Type", - "text": "Type" - }, - "<\"*\"[] | (\"ui\" | \"background_tasks\")[]>; }>" - ], - "path": "packages/core/node/core-node-server-internal/src/node_config.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - } - ] + "objects": [] } } \ No newline at end of file diff --git a/api_docs/kbn_core_node_server_internal.mdx b/api_docs/kbn_core_node_server_internal.mdx index 55fe7669e475f..965ec4d1acd3e 100644 --- a/api_docs/kbn_core_node_server_internal.mdx +++ b/api_docs/kbn_core_node_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-internal title: "@kbn/core-node-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-internal plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-internal'] --- import kbnCoreNodeServerInternalObj from './kbn_core_node_server_internal.devdocs.json'; @@ -21,13 +21,10 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 7 | 0 | 6 | 1 | +| 4 | 0 | 3 | 1 | ## Common -### Objects - - ### Interfaces diff --git a/api_docs/kbn_core_node_server_mocks.devdocs.json b/api_docs/kbn_core_node_server_mocks.devdocs.json index f2d7ca94bf8cf..7997492fa90e4 100644 --- a/api_docs/kbn_core_node_server_mocks.devdocs.json +++ b/api_docs/kbn_core_node_server_mocks.devdocs.json @@ -76,7 +76,7 @@ "label": "createInternalStartContract", "description": [], "signature": [ - "({ ui, backgroundTasks, }?: { ui: boolean; backgroundTasks: boolean; }) => jest.Mocked<", + "({ ui, backgroundTasks, migrator, }?: { ui: boolean; backgroundTasks: boolean; migrator: boolean; }) => jest.Mocked<", { "pluginId": "@kbn/core-node-server-internal", "scope": "common", @@ -99,7 +99,7 @@ "label": "__0", "description": [], "signature": [ - "{ ui: boolean; backgroundTasks: boolean; }" + "{ ui: boolean; backgroundTasks: boolean; migrator: boolean; }" ], "path": "packages/core/node/core-node-server-mocks/src/node_service.mock.ts", "deprecated": false, diff --git a/api_docs/kbn_core_node_server_mocks.mdx b/api_docs/kbn_core_node_server_mocks.mdx index 7d7ee7c68c4fc..ba8bd02cacbc1 100644 --- a/api_docs/kbn_core_node_server_mocks.mdx +++ b/api_docs/kbn_core_node_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-mocks title: "@kbn/core-node-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 9fd1254ab1eea..fa35561e301dc 100644 --- a/api_docs/kbn_core_notifications_browser.mdx +++ b/api_docs/kbn_core_notifications_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser title: "@kbn/core-notifications-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser plugin -date: 2023-02-23 +date: 2023-02-28 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 e9d1f32b56ad1..1401f5adcf067 100644 --- a/api_docs/kbn_core_notifications_browser_internal.mdx +++ b/api_docs/kbn_core_notifications_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-internal title: "@kbn/core-notifications-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 3cec6b3d251b9..ab530789c88d6 100644 --- a/api_docs/kbn_core_notifications_browser_mocks.mdx +++ b/api_docs/kbn_core_notifications_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-mocks title: "@kbn/core-notifications-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 fcac09dd57a8a..67fbc659c9ed1 100644 --- a/api_docs/kbn_core_overlays_browser.mdx +++ b/api_docs/kbn_core_overlays_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser title: "@kbn/core-overlays-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser plugin -date: 2023-02-23 +date: 2023-02-28 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 5cda50c141131..f4a4ac81b9b4e 100644 --- a/api_docs/kbn_core_overlays_browser_internal.mdx +++ b/api_docs/kbn_core_overlays_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-internal title: "@kbn/core-overlays-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 8cfefe67baff1..19a6cecb35764 100644 --- a/api_docs/kbn_core_overlays_browser_mocks.mdx +++ b/api_docs/kbn_core_overlays_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-mocks title: "@kbn/core-overlays-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 f65e0b9a3e246..a75e5e40ce83b 100644 --- a/api_docs/kbn_core_plugins_browser.mdx +++ b/api_docs/kbn_core_plugins_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser title: "@kbn/core-plugins-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser plugin -date: 2023-02-23 +date: 2023-02-28 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 3f68f8776ab58..5ca8c83b6ce61 100644 --- a/api_docs/kbn_core_plugins_browser_mocks.mdx +++ b/api_docs/kbn_core_plugins_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser-mocks title: "@kbn/core-plugins-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser-mocks plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser-mocks'] --- import kbnCorePluginsBrowserMocksObj from './kbn_core_plugins_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server.mdx b/api_docs/kbn_core_plugins_server.mdx index 15dc2fcf63c27..2f22103dbf192 100644 --- a/api_docs/kbn_core_plugins_server.mdx +++ b/api_docs/kbn_core_plugins_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server title: "@kbn/core-plugins-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server plugin -date: 2023-02-23 +date: 2023-02-28 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 a50a0bf8f064e..3c4c88afbd8a5 100644 --- a/api_docs/kbn_core_plugins_server_mocks.mdx +++ b/api_docs/kbn_core_plugins_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server-mocks title: "@kbn/core-plugins-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 d4c5be20b8746..94fbe3c381272 100644 --- a/api_docs/kbn_core_preboot_server.mdx +++ b/api_docs/kbn_core_preboot_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server title: "@kbn/core-preboot-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server plugin -date: 2023-02-23 +date: 2023-02-28 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 3c3680c5b6eb8..4bb4a6293a868 100644 --- a/api_docs/kbn_core_preboot_server_mocks.mdx +++ b/api_docs/kbn_core_preboot_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server-mocks title: "@kbn/core-preboot-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 4ba061fd97461..f5f81a45db236 100644 --- a/api_docs/kbn_core_rendering_browser_mocks.mdx +++ b/api_docs/kbn_core_rendering_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-browser-mocks title: "@kbn/core-rendering-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-browser-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 561f4341716e0..aa0d29f8cd612 100644 --- a/api_docs/kbn_core_rendering_server_internal.mdx +++ b/api_docs/kbn_core_rendering_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-internal title: "@kbn/core-rendering-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 4d6d87793f3db..ca6bc8c89dbf2 100644 --- a/api_docs/kbn_core_rendering_server_mocks.mdx +++ b/api_docs/kbn_core_rendering_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-mocks title: "@kbn/core-rendering-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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.devdocs.json b/api_docs/kbn_core_root_server_internal.devdocs.json index 765f11e6ad76e..c5af081041ed3 100644 --- a/api_docs/kbn_core_root_server_internal.devdocs.json +++ b/api_docs/kbn_core_root_server_internal.devdocs.json @@ -417,7 +417,43 @@ "initialIsOpen": false } ], - "functions": [], + "functions": [ + { + "parentPluginId": "@kbn/core-root-server-internal", + "id": "def-common.registerServiceConfig", + "type": "Function", + "tags": [], + "label": "registerServiceConfig", + "description": [], + "signature": [ + "(configService: ", + "ConfigService", + ") => void" + ], + "path": "packages/core/root/core-root-server-internal/src/register_service_config.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-root-server-internal", + "id": "def-common.registerServiceConfig.$1", + "type": "Object", + "tags": [], + "label": "configService", + "description": [], + "signature": [ + "ConfigService" + ], + "path": "packages/core/root/core-root-server-internal/src/register_service_config.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], "interfaces": [], "enums": [], "misc": [], diff --git a/api_docs/kbn_core_root_server_internal.mdx b/api_docs/kbn_core_root_server_internal.mdx index 40c6fe9097697..d33764a007ee1 100644 --- a/api_docs/kbn_core_root_server_internal.mdx +++ b/api_docs/kbn_core_root_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-root-server-internal title: "@kbn/core-root-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-root-server-internal plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-root-server-internal'] --- import kbnCoreRootServerInternalObj from './kbn_core_root_server_internal.devdocs.json'; @@ -21,10 +21,13 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 23 | 1 | 22 | 0 | +| 25 | 1 | 24 | 0 | ## Common +### Functions + + ### Classes diff --git a/api_docs/kbn_core_saved_objects_api_browser.mdx b/api_docs/kbn_core_saved_objects_api_browser.mdx index 24bb4b6c2dc58..9925c7d54e924 100644 --- a/api_docs/kbn_core_saved_objects_api_browser.mdx +++ b/api_docs/kbn_core_saved_objects_api_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-browser title: "@kbn/core-saved-objects-api-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-browser plugin -date: 2023-02-23 +date: 2023-02-28 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 505b96716d48b..371aec9bdd901 100644 --- a/api_docs/kbn_core_saved_objects_api_server.mdx +++ b/api_docs/kbn_core_saved_objects_api_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server title: "@kbn/core-saved-objects-api-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server'] --- import kbnCoreSavedObjectsApiServerObj from './kbn_core_saved_objects_api_server.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server_internal.mdx b/api_docs/kbn_core_saved_objects_api_server_internal.mdx index 12624d3c2ffa1..52030b02bde3d 100644 --- a/api_docs/kbn_core_saved_objects_api_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-internal title: "@kbn/core-saved-objects-api-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-internal plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-internal'] --- import kbnCoreSavedObjectsApiServerInternalObj from './kbn_core_saved_objects_api_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx index 803e96d1d5a38..297fa858fc441 100644 --- a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-mocks title: "@kbn/core-saved-objects-api-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 89b711814980d..7e50fa4187ed5 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 @@ -70,7 +70,7 @@ "label": "migration", "description": [], "signature": [ - "{ readonly discardUnknownObjects?: string | undefined; readonly discardCorruptObjects?: string | undefined; readonly skip: boolean; readonly pollInterval: number; readonly batchSize: number; readonly maxBatchSizeBytes: ", + "{ readonly discardUnknownObjects?: string | undefined; readonly discardCorruptObjects?: string | undefined; readonly skip: boolean; readonly pollInterval: number; readonly algorithm: \"v2\" | \"zdt\"; readonly batchSize: number; readonly maxBatchSizeBytes: ", { "pluginId": "@kbn/config-schema", "scope": "common", @@ -129,7 +129,7 @@ "label": "rawMigrationConfig", "description": [], "signature": [ - "Readonly<{ discardUnknownObjects?: string | undefined; discardCorruptObjects?: string | undefined; } & { skip: boolean; pollInterval: number; batchSize: number; maxBatchSizeBytes: ", + "Readonly<{ discardUnknownObjects?: string | undefined; discardCorruptObjects?: string | undefined; } & { skip: boolean; pollInterval: number; algorithm: \"v2\" | \"zdt\"; batchSize: number; maxBatchSizeBytes: ", { "pluginId": "@kbn/config-schema", "scope": "common", @@ -755,7 +755,7 @@ "label": "SavedObjectsMigrationConfigType", "description": [], "signature": [ - "{ readonly discardUnknownObjects?: string | undefined; readonly discardCorruptObjects?: string | undefined; readonly skip: boolean; readonly pollInterval: number; readonly batchSize: number; readonly maxBatchSizeBytes: ", + "{ readonly discardUnknownObjects?: string | undefined; readonly discardCorruptObjects?: string | undefined; readonly skip: boolean; readonly pollInterval: number; readonly algorithm: \"v2\" | \"zdt\"; readonly batchSize: number; readonly maxBatchSizeBytes: ", { "pluginId": "@kbn/config-schema", "scope": "common", @@ -887,7 +887,15 @@ "section": "def-common.ObjectType", "text": "ObjectType" }, - "<{ batchSize: ", + "<{ algorithm: ", + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.Type", + "text": "Type" + }, + "<\"v2\" | \"zdt\">; batchSize: ", { "pluginId": "@kbn/config-schema", "scope": "common", 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 b972bd8f66746..5b47d36f7b4e3 100644 --- a/api_docs/kbn_core_saved_objects_base_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-internal title: "@kbn/core-saved-objects-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-internal plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-internal'] --- import kbnCoreSavedObjectsBaseServerInternalObj from './kbn_core_saved_objects_base_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx index 7f9c8d3f5503d..08754e85a429e 100644 --- a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-mocks title: "@kbn/core-saved-objects-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 e3dc0f69fa4c4..3f72172ad2624 100644 --- a/api_docs/kbn_core_saved_objects_browser.mdx +++ b/api_docs/kbn_core_saved_objects_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser title: "@kbn/core-saved-objects-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser plugin -date: 2023-02-23 +date: 2023-02-28 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 394d67f9f652d..0e2f1a128dc24 100644 --- a/api_docs/kbn_core_saved_objects_browser_internal.mdx +++ b/api_docs/kbn_core_saved_objects_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-internal title: "@kbn/core-saved-objects-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 fa916a349ec71..e9cad4c0e296d 100644 --- a/api_docs/kbn_core_saved_objects_browser_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-mocks title: "@kbn/core-saved-objects-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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.devdocs.json b/api_docs/kbn_core_saved_objects_common.devdocs.json index 6a9c331a3411c..0c231f1ff4878 100644 --- a/api_docs/kbn_core_saved_objects_common.devdocs.json +++ b/api_docs/kbn_core_saved_objects_common.devdocs.json @@ -1275,14 +1275,6 @@ "plugin": "dataViews", "path": "src/plugins/data_views/server/utils.ts" }, - { - "plugin": "discover", - "path": "src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts" - }, - { - "plugin": "discover", - "path": "src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts" - }, { "plugin": "dataViews", "path": "src/plugins/data_views/public/saved_objects_client_wrapper.ts" @@ -3292,6 +3284,14 @@ "plugin": "infra", "path": "x-pack/plugins/infra/public/common/visualizations/lens/hosts/memory.ts" }, + { + "plugin": "infra", + "path": "x-pack/plugins/infra/public/common/visualizations/lens/hosts/memory_available.ts" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/infra/public/common/visualizations/lens/hosts/memory_available.ts" + }, { "plugin": "infra", "path": "x-pack/plugins/infra/public/common/visualizations/lens/hosts/rx.ts" diff --git a/api_docs/kbn_core_saved_objects_common.mdx b/api_docs/kbn_core_saved_objects_common.mdx index 141435a6d687e..abdd31b8265e9 100644 --- a/api_docs/kbn_core_saved_objects_common.mdx +++ b/api_docs/kbn_core_saved_objects_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-common title: "@kbn/core-saved-objects-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-common plugin -date: 2023-02-23 +date: 2023-02-28 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 3d81c1ddd55c2..e2eacb6b852ff 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-internal title: "@kbn/core-saved-objects-import-export-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 3dbaf45c5cbe0..c386a8bac46dd 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-mocks title: "@kbn/core-saved-objects-import-export-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-mocks plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-import-export-server-mocks'] --- import kbnCoreSavedObjectsImportExportServerMocksObj from './kbn_core_saved_objects_import_export_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_migration_server_internal.devdocs.json b/api_docs/kbn_core_saved_objects_migration_server_internal.devdocs.json index 68bab1e33dbd5..0134af64ddd5c 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_internal.devdocs.json +++ b/api_docs/kbn_core_saved_objects_migration_server_internal.devdocs.json @@ -599,6 +599,57 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/core-saved-objects-migration-server-internal", + "id": "def-common.buildTypesMappings", + "type": "Function", + "tags": [], + "label": "buildTypesMappings", + "description": [ + "\nMerge mappings from all registered saved object types." + ], + "signature": [ + "(types: ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectsType", + "text": "SavedObjectsType" + }, + "[]) => ", + "SavedObjectsTypeMappingDefinitions" + ], + "path": "packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/build_types_mappings.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-migration-server-internal", + "id": "def-common.buildTypesMappings.$1", + "type": "Array", + "tags": [], + "label": "types", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectsType", + "text": "SavedObjectsType" + }, + "[]" + ], + "path": "packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/build_types_mappings.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/core-saved-objects-migration-server-internal", "id": "def-common.bulkOverwriteTransformedDocuments", @@ -609,7 +660,7 @@ "\nWrite the up-to-date transformed documents to the index, overwriting any\ndocuments that are still on their outdated version." ], "signature": [ - "({ client, index, transformedDocs, refresh, }: ", + "({ client, index, operations, refresh, }: ", "BulkOverwriteTransformedDocumentsParams", ") => ", "TaskEither", @@ -632,7 +683,7 @@ "id": "def-common.bulkOverwriteTransformedDocuments.$1", "type": "Object", "tags": [], - "label": "{\n client,\n index,\n transformedDocs,\n refresh = false,\n }", + "label": "{\n client,\n index,\n operations,\n refresh = false,\n }", "description": [], "signature": [ "BulkOverwriteTransformedDocumentsParams" @@ -816,6 +867,92 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/core-saved-objects-migration-server-internal", + "id": "def-common.createBulkDeleteOperationBody", + "type": "Function", + "tags": [], + "label": "createBulkDeleteOperationBody", + "description": [ + "\nGiven a document id, creates a valid body to delete the document using the Bulk API." + ], + "signature": [ + "(_id: string) => ", + "BulkOperationContainer" + ], + "path": "packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/helpers.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-migration-server-internal", + "id": "def-common.createBulkDeleteOperationBody.$1", + "type": "string", + "tags": [], + "label": "_id", + "description": [], + "signature": [ + "string" + ], + "path": "packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-migration-server-internal", + "id": "def-common.createBulkIndexOperationTuple", + "type": "Function", + "tags": [], + "label": "createBulkIndexOperationTuple", + "description": [ + "\nGiven a document, creates a valid body to index the document using the Bulk API." + ], + "signature": [ + "(doc: ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectsRawDoc", + "text": "SavedObjectsRawDoc" + }, + ") => ", + "BulkIndexOperationTuple" + ], + "path": "packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/helpers.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-migration-server-internal", + "id": "def-common.createBulkIndexOperationTuple.$1", + "type": "Object", + "tags": [], + "label": "doc", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectsRawDoc", + "text": "SavedObjectsRawDoc" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/core-saved-objects-migration-server-internal", "id": "def-common.createIndex", @@ -1091,57 +1228,6 @@ "returnComment": [], "initialIsOpen": false }, - { - "parentPluginId": "@kbn/core-saved-objects-migration-server-internal", - "id": "def-common.mergeTypes", - "type": "Function", - "tags": [], - "label": "mergeTypes", - "description": [ - "\nMerges savedObjectMappings properties into a single object, verifying that\nno mappings are redefined." - ], - "signature": [ - "(types: ", - { - "pluginId": "@kbn/core-saved-objects-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsServerPluginApi", - "section": "def-common.SavedObjectsType", - "text": "SavedObjectsType" - }, - "[]) => ", - "SavedObjectsTypeMappingDefinitions" - ], - "path": "packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-migration-server-internal", - "id": "def-common.mergeTypes.$1", - "type": "Array", - "tags": [], - "label": "types", - "description": [], - "signature": [ - { - "pluginId": "@kbn/core-saved-objects-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsServerPluginApi", - "section": "def-common.SavedObjectsType", - "text": "SavedObjectsType" - }, - "[]" - ], - "path": "packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, { "parentPluginId": "@kbn/core-saved-objects-migration-server-internal", "id": "def-common.openPit", @@ -3335,7 +3421,7 @@ "label": "soMigrationsConfig", "description": [], "signature": [ - "{ readonly discardUnknownObjects?: string | undefined; readonly discardCorruptObjects?: string | undefined; readonly skip: boolean; readonly pollInterval: number; readonly batchSize: number; readonly maxBatchSizeBytes: ", + "{ readonly discardUnknownObjects?: string | undefined; readonly discardCorruptObjects?: string | undefined; readonly skip: boolean; readonly pollInterval: number; readonly algorithm: \"v2\" | \"zdt\"; readonly batchSize: number; readonly maxBatchSizeBytes: ", { "pluginId": "@kbn/config-schema", "scope": "common", 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 206385e4d6059..da9c17cfc84dc 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-internal title: "@kbn/core-saved-objects-migration-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-internal plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-internal'] --- import kbnCoreSavedObjectsMigrationServerInternalObj from './kbn_core_saved_objects_migration_server_internal.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 112 | 0 | 79 | 45 | +| 116 | 0 | 81 | 46 | ## Common diff --git a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx index a7dfef8f380b7..b7ff18bfe97db 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-mocks title: "@kbn/core-saved-objects-migration-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 fdd857fee6e58..3708f94bd910b 100644 --- a/api_docs/kbn_core_saved_objects_server.mdx +++ b/api_docs/kbn_core_saved_objects_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server title: "@kbn/core-saved-objects-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server plugin -date: 2023-02-23 +date: 2023-02-28 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 f464b010bc185..2af33c48974f5 100644 --- a/api_docs/kbn_core_saved_objects_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-internal title: "@kbn/core-saved-objects-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 74e7861d56233..c63669a42313a 100644 --- a/api_docs/kbn_core_saved_objects_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-mocks title: "@kbn/core-saved-objects-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 d6bc6749c588a..30311cf44a269 100644 --- a/api_docs/kbn_core_saved_objects_utils_server.mdx +++ b/api_docs/kbn_core_saved_objects_utils_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-utils-server title: "@kbn/core-saved-objects-utils-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-utils-server plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-utils-server'] --- import kbnCoreSavedObjectsUtilsServerObj from './kbn_core_saved_objects_utils_server.devdocs.json'; diff --git a/api_docs/kbn_core_status_common.mdx b/api_docs/kbn_core_status_common.mdx index 63182ab34a9a7..c4464831aa731 100644 --- a/api_docs/kbn_core_status_common.mdx +++ b/api_docs/kbn_core_status_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common title: "@kbn/core-status-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common plugin -date: 2023-02-23 +date: 2023-02-28 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 facf6ab1cebd3..967ee90a4af04 100644 --- a/api_docs/kbn_core_status_common_internal.mdx +++ b/api_docs/kbn_core_status_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common-internal title: "@kbn/core-status-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 40db621a15ee5..36ab7c3c06d35 100644 --- a/api_docs/kbn_core_status_server.mdx +++ b/api_docs/kbn_core_status_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server title: "@kbn/core-status-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server plugin -date: 2023-02-23 +date: 2023-02-28 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 7c6801f71f239..294abb3b0c19a 100644 --- a/api_docs/kbn_core_status_server_internal.mdx +++ b/api_docs/kbn_core_status_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-internal title: "@kbn/core-status-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 7ebe6b4b883f7..844f75cab333a 100644 --- a/api_docs/kbn_core_status_server_mocks.mdx +++ b/api_docs/kbn_core_status_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-mocks title: "@kbn/core-status-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 302461085b2b7..5639a6c086857 100644 --- a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx +++ b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-deprecations-getters title: "@kbn/core-test-helpers-deprecations-getters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-deprecations-getters plugin -date: 2023-02-23 +date: 2023-02-28 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 130daa2e822a9..5c0b770e2c434 100644 --- a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx +++ b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-http-setup-browser title: "@kbn/core-test-helpers-http-setup-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-http-setup-browser plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-http-setup-browser'] --- import kbnCoreTestHelpersHttpSetupBrowserObj from './kbn_core_test_helpers_http_setup_browser.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_kbn_server.mdx b/api_docs/kbn_core_test_helpers_kbn_server.mdx index 62d2469af8069..d9e9ac63049ae 100644 --- a/api_docs/kbn_core_test_helpers_kbn_server.mdx +++ b/api_docs/kbn_core_test_helpers_kbn_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-kbn-server title: "@kbn/core-test-helpers-kbn-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-kbn-server plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-kbn-server'] --- import kbnCoreTestHelpersKbnServerObj from './kbn_core_test_helpers_kbn_server.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx index a2f69c0ec786e..d9fbb1ee32328 100644 --- a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx +++ b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-so-type-serializer title: "@kbn/core-test-helpers-so-type-serializer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-so-type-serializer plugin -date: 2023-02-23 +date: 2023-02-28 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 92452e33e86f0..a0448c7fa78bf 100644 --- a/api_docs/kbn_core_test_helpers_test_utils.mdx +++ b/api_docs/kbn_core_test_helpers_test_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-test-utils title: "@kbn/core-test-helpers-test-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-test-utils plugin -date: 2023-02-23 +date: 2023-02-28 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 993b2e78a9755..04bfe87b1f780 100644 --- a/api_docs/kbn_core_theme_browser.mdx +++ b/api_docs/kbn_core_theme_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser title: "@kbn/core-theme-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser'] --- import kbnCoreThemeBrowserObj from './kbn_core_theme_browser.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_internal.mdx b/api_docs/kbn_core_theme_browser_internal.mdx index 0796c6b1353d5..1eb0414dd45c8 100644 --- a/api_docs/kbn_core_theme_browser_internal.mdx +++ b/api_docs/kbn_core_theme_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-internal title: "@kbn/core-theme-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-internal plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-internal'] --- import kbnCoreThemeBrowserInternalObj from './kbn_core_theme_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_mocks.mdx b/api_docs/kbn_core_theme_browser_mocks.mdx index c25641aa2fe18..8c09095a3e3b5 100644 --- a/api_docs/kbn_core_theme_browser_mocks.mdx +++ b/api_docs/kbn_core_theme_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-mocks title: "@kbn/core-theme-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 de27e2046d976..822f2d74702df 100644 --- a/api_docs/kbn_core_ui_settings_browser.mdx +++ b/api_docs/kbn_core_ui_settings_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser title: "@kbn/core-ui-settings-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser plugin -date: 2023-02-23 +date: 2023-02-28 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 694f3717600d8..28564e1638de5 100644 --- a/api_docs/kbn_core_ui_settings_browser_internal.mdx +++ b/api_docs/kbn_core_ui_settings_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-internal title: "@kbn/core-ui-settings-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 15aef80cefb14..14a983d39a21d 100644 --- a/api_docs/kbn_core_ui_settings_browser_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-mocks title: "@kbn/core-ui-settings-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 1a134bfef57bf..e7e4dc709985f 100644 --- a/api_docs/kbn_core_ui_settings_common.mdx +++ b/api_docs/kbn_core_ui_settings_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-common title: "@kbn/core-ui-settings-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-common plugin -date: 2023-02-23 +date: 2023-02-28 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 b57ebb9bdd7eb..f1f131ca9ae40 100644 --- a/api_docs/kbn_core_ui_settings_server.mdx +++ b/api_docs/kbn_core_ui_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server title: "@kbn/core-ui-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server plugin -date: 2023-02-23 +date: 2023-02-28 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 af49740d2b8af..322a944c92407 100644 --- a/api_docs/kbn_core_ui_settings_server_internal.mdx +++ b/api_docs/kbn_core_ui_settings_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-internal title: "@kbn/core-ui-settings-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 e406d059ecc07..972e4fea27a2b 100644 --- a/api_docs/kbn_core_ui_settings_server_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-mocks title: "@kbn/core-ui-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 fa60a4dec9bb7..ad1a2af0c26dd 100644 --- a/api_docs/kbn_core_usage_data_server.mdx +++ b/api_docs/kbn_core_usage_data_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server title: "@kbn/core-usage-data-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server plugin -date: 2023-02-23 +date: 2023-02-28 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 77ca6970ff212..7986cb3e189ac 100644 --- a/api_docs/kbn_core_usage_data_server_internal.mdx +++ b/api_docs/kbn_core_usage_data_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-internal title: "@kbn/core-usage-data-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-internal plugin -date: 2023-02-23 +date: 2023-02-28 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 d461a181cb519..948602f8b16ce 100644 --- a/api_docs/kbn_core_usage_data_server_mocks.mdx +++ b/api_docs/kbn_core_usage_data_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-mocks title: "@kbn/core-usage-data-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-mocks plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-mocks'] --- import kbnCoreUsageDataServerMocksObj from './kbn_core_usage_data_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_crypto.mdx b/api_docs/kbn_crypto.mdx index 3dd34089fa597..a1939bcf16477 100644 --- a/api_docs/kbn_crypto.mdx +++ b/api_docs/kbn_crypto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto title: "@kbn/crypto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto plugin -date: 2023-02-23 +date: 2023-02-28 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 f928e7f9028e6..3e6ba58a589e0 100644 --- a/api_docs/kbn_crypto_browser.mdx +++ b/api_docs/kbn_crypto_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto-browser title: "@kbn/crypto-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto-browser plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto-browser'] --- import kbnCryptoBrowserObj from './kbn_crypto_browser.devdocs.json'; diff --git a/api_docs/kbn_cypress_config.mdx b/api_docs/kbn_cypress_config.mdx index fc4866894b6e2..cd7c5ec1315f8 100644 --- a/api_docs/kbn_cypress_config.mdx +++ b/api_docs/kbn_cypress_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cypress-config title: "@kbn/cypress-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cypress-config plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cypress-config'] --- import kbnCypressConfigObj from './kbn_cypress_config.devdocs.json'; diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index 09f1decdc206e..dc4e0302a3840 100644 --- a/api_docs/kbn_datemath.mdx +++ b/api_docs/kbn_datemath.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-datemath title: "@kbn/datemath" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/datemath plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/datemath'] --- import kbnDatemathObj from './kbn_datemath.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_errors.mdx b/api_docs/kbn_dev_cli_errors.mdx index 461060c14979d..7701ace42bb65 100644 --- a/api_docs/kbn_dev_cli_errors.mdx +++ b/api_docs/kbn_dev_cli_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-errors title: "@kbn/dev-cli-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-errors plugin -date: 2023-02-23 +date: 2023-02-28 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 dd89feb4f29ce..ee43d20b0961e 100644 --- a/api_docs/kbn_dev_cli_runner.mdx +++ b/api_docs/kbn_dev_cli_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-runner title: "@kbn/dev-cli-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-runner plugin -date: 2023-02-23 +date: 2023-02-28 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 fce7736d25518..3d9236ee8431d 100644 --- a/api_docs/kbn_dev_proc_runner.mdx +++ b/api_docs/kbn_dev_proc_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-proc-runner title: "@kbn/dev-proc-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-proc-runner plugin -date: 2023-02-23 +date: 2023-02-28 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 b5208c8ad0d2d..a4503d762f0c2 100644 --- a/api_docs/kbn_dev_utils.mdx +++ b/api_docs/kbn_dev_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-utils title: "@kbn/dev-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-utils plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index 03bb0f734d087..690befe3cbb83 100644 --- a/api_docs/kbn_doc_links.mdx +++ b/api_docs/kbn_doc_links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-doc-links title: "@kbn/doc-links" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/doc-links plugin -date: 2023-02-23 +date: 2023-02-28 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 6e2ab4acb08f4..0399b4f8c746f 100644 --- a/api_docs/kbn_docs_utils.mdx +++ b/api_docs/kbn_docs_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-docs-utils title: "@kbn/docs-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/docs-utils plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/docs-utils'] --- import kbnDocsUtilsObj from './kbn_docs_utils.devdocs.json'; diff --git a/api_docs/kbn_ebt_tools.mdx b/api_docs/kbn_ebt_tools.mdx index c1a28b43b4c1c..4657ed79be42d 100644 --- a/api_docs/kbn_ebt_tools.mdx +++ b/api_docs/kbn_ebt_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ebt-tools title: "@kbn/ebt-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ebt-tools plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] --- import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; diff --git a/api_docs/kbn_ecs.mdx b/api_docs/kbn_ecs.mdx index 6726d5e819fa8..bd5b4c146878d 100644 --- a/api_docs/kbn_ecs.mdx +++ b/api_docs/kbn_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs title: "@kbn/ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs'] --- import kbnEcsObj from './kbn_ecs.devdocs.json'; diff --git a/api_docs/kbn_ecs_data_quality_dashboard.mdx b/api_docs/kbn_ecs_data_quality_dashboard.mdx index c73fed2e9a410..8fe18eca0bc56 100644 --- a/api_docs/kbn_ecs_data_quality_dashboard.mdx +++ b/api_docs/kbn_ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs-data-quality-dashboard title: "@kbn/ecs-data-quality-dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs-data-quality-dashboard plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs-data-quality-dashboard'] --- import kbnEcsDataQualityDashboardObj from './kbn_ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/kbn_es.mdx b/api_docs/kbn_es.mdx index 0d21d8300b668..ba011e6bbf167 100644 --- a/api_docs/kbn_es.mdx +++ b/api_docs/kbn_es.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es title: "@kbn/es" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es plugin -date: 2023-02-23 +date: 2023-02-28 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 9dacb17fbc646..9421f34bd8b83 100644 --- a/api_docs/kbn_es_archiver.mdx +++ b/api_docs/kbn_es_archiver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-archiver title: "@kbn/es-archiver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-archiver plugin -date: 2023-02-23 +date: 2023-02-28 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 4471d748be756..fe513e762d0e9 100644 --- a/api_docs/kbn_es_errors.mdx +++ b/api_docs/kbn_es_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-errors title: "@kbn/es-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-errors plugin -date: 2023-02-23 +date: 2023-02-28 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 071cfc24eb892..e0903de48be62 100644 --- a/api_docs/kbn_es_query.mdx +++ b/api_docs/kbn_es_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-query title: "@kbn/es-query" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-query plugin -date: 2023-02-23 +date: 2023-02-28 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 cdd31ef9d8ede..847febd25c24d 100644 --- a/api_docs/kbn_es_types.mdx +++ b/api_docs/kbn_es_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-types title: "@kbn/es-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-types plugin -date: 2023-02-23 +date: 2023-02-28 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 679a4387df2ed..d399dc95f270c 100644 --- a/api_docs/kbn_eslint_plugin_imports.mdx +++ b/api_docs/kbn_eslint_plugin_imports.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-eslint-plugin-imports title: "@kbn/eslint-plugin-imports" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/eslint-plugin-imports plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_expandable_flyout.devdocs.json b/api_docs/kbn_expandable_flyout.devdocs.json new file mode 100644 index 0000000000000..9658f381fd343 --- /dev/null +++ b/api_docs/kbn_expandable_flyout.devdocs.json @@ -0,0 +1,273 @@ +{ + "id": "@kbn/expandable-flyout", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyout", + "type": "Function", + "tags": [], + "label": "ExpandableFlyout", + "description": [ + "\nExpandable flyout UI React component.\nDisplays 3 sections (right, left, preview) depending on the panels in the context." + ], + "signature": [ + "{ ({ registeredPanels, handleOnFlyoutClosed, ...flyoutProps }: React.PropsWithChildren<", + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.ExpandableFlyoutProps", + "text": "ExpandableFlyoutProps" + }, + ">): JSX.Element; displayName: string | undefined; }" + ], + "path": "packages/kbn-expandable-flyout/src/index.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyout.$1", + "type": "CompoundType", + "tags": [], + "label": "{\n registeredPanels,\n handleOnFlyoutClosed,\n ...flyoutProps\n}", + "description": [], + "signature": [ + "React.PropsWithChildren<", + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.ExpandableFlyoutProps", + "text": "ExpandableFlyoutProps" + }, + ">" + ], + "path": "packages/kbn-expandable-flyout/src/index.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutProvider", + "type": "Function", + "tags": [], + "label": "ExpandableFlyoutProvider", + "description": [ + "\nWrap your plugin with this context for the ExpandableFlyout React component." + ], + "signature": [ + "({ children }: ", + "ExpandableFlyoutProviderProps", + ") => JSX.Element" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutProvider.$1", + "type": "Object", + "tags": [], + "label": "{ children }", + "description": [], + "signature": [ + "ExpandableFlyoutProviderProps" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.useExpandableFlyoutContext", + "type": "Function", + "tags": [], + "label": "useExpandableFlyoutContext", + "description": [ + "\nRetrieve context's properties" + ], + "signature": [ + "() => ", + "ExpandableFlyoutContext" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [ + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutProps", + "type": "Interface", + "tags": [], + "label": "ExpandableFlyoutProps", + "description": [], + "signature": [ + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.ExpandableFlyoutProps", + "text": "ExpandableFlyoutProps" + }, + " extends ", + "EuiFlyoutProps", + "<\"div\">" + ], + "path": "packages/kbn-expandable-flyout/src/index.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutProps.registeredPanels", + "type": "Array", + "tags": [], + "label": "registeredPanels", + "description": [ + "\nList of all registered panels available for render" + ], + "signature": [ + "Panel", + "[]" + ], + "path": "packages/kbn-expandable-flyout/src/index.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutProps.handleOnFlyoutClosed", + "type": "Function", + "tags": [], + "label": "handleOnFlyoutClosed", + "description": [ + "\nPropagate out EuiFlyout onClose event" + ], + "signature": [ + "(() => void) | undefined" + ], + "path": "packages/kbn-expandable-flyout/src/index.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.FlyoutPanel", + "type": "Interface", + "tags": [], + "label": "FlyoutPanel", + "description": [], + "path": "packages/kbn-expandable-flyout/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.FlyoutPanel.id", + "type": "string", + "tags": [], + "label": "id", + "description": [ + "\nUnique key to identify the panel" + ], + "path": "packages/kbn-expandable-flyout/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.FlyoutPanel.params", + "type": "Object", + "tags": [], + "label": "params", + "description": [ + "\nAny parameters necessary for the initial requests within the flyout" + ], + "signature": [ + "Record | undefined" + ], + "path": "packages/kbn-expandable-flyout/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.FlyoutPanel.path", + "type": "Array", + "tags": [], + "label": "path", + "description": [ + "\nTracks the path for what to show in a panel. We may have multiple tabs or details..., so easiest to just use a stack" + ], + "signature": [ + "string[] | undefined" + ], + "path": "packages/kbn-expandable-flyout/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.FlyoutPanel.state", + "type": "Object", + "tags": [], + "label": "state", + "description": [ + "\nTracks visual state such as whether the panel is collapsed" + ], + "signature": [ + "Record | undefined" + ], + "path": "packages/kbn-expandable-flyout/src/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_expandable_flyout.mdx b/api_docs/kbn_expandable_flyout.mdx new file mode 100644 index 0000000000000..22fbc24cf88a1 --- /dev/null +++ b/api_docs/kbn_expandable_flyout.mdx @@ -0,0 +1,33 @@ +--- +#### +#### 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: kibKbnExpandableFlyoutPluginApi +slug: /kibana-dev-docs/api/kbn-expandable-flyout +title: "@kbn/expandable-flyout" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/expandable-flyout plugin +date: 2023-02-28 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/expandable-flyout'] +--- +import kbnExpandableFlyoutObj from './kbn_expandable_flyout.devdocs.json'; + + + +Contact [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 13 | 0 | 4 | 3 | + +## Common + +### Functions + + +### Interfaces + + diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx index 583258dba7122..e6ea6c2915b92 100644 --- a/api_docs/kbn_field_types.mdx +++ b/api_docs/kbn_field_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-types title: "@kbn/field-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-types plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-types'] --- import kbnFieldTypesObj from './kbn_field_types.devdocs.json'; diff --git a/api_docs/kbn_find_used_node_modules.mdx b/api_docs/kbn_find_used_node_modules.mdx index 8c37c25cb3636..67f7f707c8648 100644 --- a/api_docs/kbn_find_used_node_modules.mdx +++ b/api_docs/kbn_find_used_node_modules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-find-used-node-modules title: "@kbn/find-used-node-modules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/find-used-node-modules plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/find-used-node-modules'] --- import kbnFindUsedNodeModulesObj from './kbn_find_used_node_modules.devdocs.json'; diff --git a/api_docs/kbn_ftr_common_functional_services.mdx b/api_docs/kbn_ftr_common_functional_services.mdx index d704f0efaf8b3..9ac919b0a22a4 100644 --- a/api_docs/kbn_ftr_common_functional_services.mdx +++ b/api_docs/kbn_ftr_common_functional_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-services title: "@kbn/ftr-common-functional-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-services plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ftr-common-functional-services'] --- import kbnFtrCommonFunctionalServicesObj from './kbn_ftr_common_functional_services.devdocs.json'; diff --git a/api_docs/kbn_generate.mdx b/api_docs/kbn_generate.mdx index cd1aefef208f7..938c566cab0ad 100644 --- a/api_docs/kbn_generate.mdx +++ b/api_docs/kbn_generate.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate title: "@kbn/generate" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] --- import kbnGenerateObj from './kbn_generate.devdocs.json'; diff --git a/api_docs/kbn_guided_onboarding.mdx b/api_docs/kbn_guided_onboarding.mdx index 581b3654b2946..15e3f4473cc4d 100644 --- a/api_docs/kbn_guided_onboarding.mdx +++ b/api_docs/kbn_guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-guided-onboarding title: "@kbn/guided-onboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/guided-onboarding plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/guided-onboarding'] --- import kbnGuidedOnboardingObj from './kbn_guided_onboarding.devdocs.json'; diff --git a/api_docs/kbn_handlebars.devdocs.json b/api_docs/kbn_handlebars.devdocs.json index 0c3d26cf1c175..84069ecfa5b9c 100644 --- a/api_docs/kbn_handlebars.devdocs.json +++ b/api_docs/kbn_handlebars.devdocs.json @@ -420,7 +420,7 @@ "\nSupported Handlebars compile options.\n\nThis is a subset of all the compile options supported by the upstream\nHandlebars module." ], "signature": [ - "{ data?: boolean | undefined; strict?: boolean | undefined; knownHelpers?: KnownHelpers | undefined; knownHelpersOnly?: boolean | undefined; noEscape?: boolean | undefined; assumeObjects?: boolean | undefined; preventIndent?: boolean | undefined; explicitPartialContext?: boolean | undefined; }" + "{ strict?: boolean | undefined; data?: boolean | undefined; knownHelpers?: KnownHelpers | undefined; knownHelpersOnly?: boolean | undefined; noEscape?: boolean | undefined; assumeObjects?: boolean | undefined; preventIndent?: boolean | undefined; explicitPartialContext?: boolean | undefined; }" ], "path": "packages/kbn-handlebars/src/types.ts", "deprecated": false, diff --git a/api_docs/kbn_handlebars.mdx b/api_docs/kbn_handlebars.mdx index e671389120c27..d52887d5f386a 100644 --- a/api_docs/kbn_handlebars.mdx +++ b/api_docs/kbn_handlebars.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-handlebars title: "@kbn/handlebars" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/handlebars plugin -date: 2023-02-23 +date: 2023-02-28 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 6c30f850b36be..2f6f53b29bcc8 100644 --- a/api_docs/kbn_hapi_mocks.mdx +++ b/api_docs/kbn_hapi_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-hapi-mocks title: "@kbn/hapi-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/hapi-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 be1638f20c517..3c704dc1082d2 100644 --- a/api_docs/kbn_health_gateway_server.mdx +++ b/api_docs/kbn_health_gateway_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-health-gateway-server title: "@kbn/health-gateway-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/health-gateway-server plugin -date: 2023-02-23 +date: 2023-02-28 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 64d2ba1c4d4af..d2449d916ce71 100644 --- a/api_docs/kbn_home_sample_data_card.mdx +++ b/api_docs/kbn_home_sample_data_card.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-card title: "@kbn/home-sample-data-card" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-card plugin -date: 2023-02-23 +date: 2023-02-28 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 eaf8fda717447..2b2abfa098b6b 100644 --- a/api_docs/kbn_home_sample_data_tab.mdx +++ b/api_docs/kbn_home_sample_data_tab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-tab title: "@kbn/home-sample-data-tab" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-tab plugin -date: 2023-02-23 +date: 2023-02-28 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 7e7f227e87f98..ccc93fbc3ff29 100644 --- a/api_docs/kbn_i18n.mdx +++ b/api_docs/kbn_i18n.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n title: "@kbn/i18n" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n plugin -date: 2023-02-23 +date: 2023-02-28 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 aa0078ef5c5ac..2c78806ed6e46 100644 --- a/api_docs/kbn_i18n_react.mdx +++ b/api_docs/kbn_i18n_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n-react title: "@kbn/i18n-react" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n-react plugin -date: 2023-02-23 +date: 2023-02-28 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 b6f3147decfad..72bd7cf1cb0f0 100644 --- a/api_docs/kbn_import_resolver.mdx +++ b/api_docs/kbn_import_resolver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-import-resolver title: "@kbn/import-resolver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/import-resolver plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_interpreter.mdx b/api_docs/kbn_interpreter.mdx index d77a53da11555..50d4412ff5b81 100644 --- a/api_docs/kbn_interpreter.mdx +++ b/api_docs/kbn_interpreter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-interpreter title: "@kbn/interpreter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/interpreter plugin -date: 2023-02-23 +date: 2023-02-28 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 2d2f750381a0a..20a8be80b5c93 100644 --- a/api_docs/kbn_io_ts_utils.mdx +++ b/api_docs/kbn_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-io-ts-utils title: "@kbn/io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/io-ts-utils plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils'] --- import kbnIoTsUtilsObj from './kbn_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx index 933b4190a49d3..93621cf47e0aa 100644 --- a/api_docs/kbn_jest_serializers.mdx +++ b/api_docs/kbn_jest_serializers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-jest-serializers title: "@kbn/jest-serializers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/jest-serializers plugin -date: 2023-02-23 +date: 2023-02-28 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 8eed4ac6845a7..d742be4326672 100644 --- a/api_docs/kbn_journeys.mdx +++ b/api_docs/kbn_journeys.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-journeys title: "@kbn/journeys" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/journeys plugin -date: 2023-02-23 +date: 2023-02-28 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 b8bd448b9cfe7..610d8fc4a18f5 100644 --- a/api_docs/kbn_json_ast.mdx +++ b/api_docs/kbn_json_ast.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-json-ast title: "@kbn/json-ast" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/json-ast plugin -date: 2023-02-23 +date: 2023-02-28 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 841efab04a221..ff7cb5480240d 100644 --- a/api_docs/kbn_kibana_manifest_schema.mdx +++ b/api_docs/kbn_kibana_manifest_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-kibana-manifest-schema title: "@kbn/kibana-manifest-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/kibana-manifest-schema plugin -date: 2023-02-23 +date: 2023-02-28 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 1159bd63d69d2..94f433a05f257 100644 --- a/api_docs/kbn_language_documentation_popover.mdx +++ b/api_docs/kbn_language_documentation_popover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-language-documentation-popover title: "@kbn/language-documentation-popover" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/language-documentation-popover plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/language-documentation-popover'] --- import kbnLanguageDocumentationPopoverObj from './kbn_language_documentation_popover.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index 508e56de5e5b0..4e0fcbffc46f9 100644 --- a/api_docs/kbn_logging.mdx +++ b/api_docs/kbn_logging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging title: "@kbn/logging" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging plugin -date: 2023-02-23 +date: 2023-02-28 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 4544baa89c341..4d5f32f707390 100644 --- a/api_docs/kbn_logging_mocks.mdx +++ b/api_docs/kbn_logging_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging-mocks title: "@kbn/logging-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging-mocks plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks'] --- import kbnLoggingMocksObj from './kbn_logging_mocks.devdocs.json'; diff --git a/api_docs/kbn_managed_vscode_config.mdx b/api_docs/kbn_managed_vscode_config.mdx index 7bc0996b658ba..6d92d1ead30b5 100644 --- a/api_docs/kbn_managed_vscode_config.mdx +++ b/api_docs/kbn_managed_vscode_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-vscode-config title: "@kbn/managed-vscode-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-vscode-config plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-vscode-config'] --- import kbnManagedVscodeConfigObj from './kbn_managed_vscode_config.devdocs.json'; diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index a7abbf3bc90df..7a15ccfa87ed5 100644 --- a/api_docs/kbn_mapbox_gl.mdx +++ b/api_docs/kbn_mapbox_gl.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mapbox-gl title: "@kbn/mapbox-gl" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mapbox-gl plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mapbox-gl'] --- import kbnMapboxGlObj from './kbn_mapbox_gl.devdocs.json'; diff --git a/api_docs/kbn_ml_agg_utils.mdx b/api_docs/kbn_ml_agg_utils.mdx index 5bfd38d63a337..3a50b4adcfb53 100644 --- a/api_docs/kbn_ml_agg_utils.mdx +++ b/api_docs/kbn_ml_agg_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-agg-utils title: "@kbn/ml-agg-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-agg-utils plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-agg-utils'] --- import kbnMlAggUtilsObj from './kbn_ml_agg_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_date_picker.mdx b/api_docs/kbn_ml_date_picker.mdx index 36abc32ff96f1..dfeeb1510c9f7 100644 --- a/api_docs/kbn_ml_date_picker.mdx +++ b/api_docs/kbn_ml_date_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-picker title: "@kbn/ml-date-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-picker plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-date-picker'] --- import kbnMlDatePickerObj from './kbn_ml_date_picker.devdocs.json'; diff --git a/api_docs/kbn_ml_is_defined.mdx b/api_docs/kbn_ml_is_defined.mdx index 8c16cbb24bb4d..6874332a26f30 100644 --- a/api_docs/kbn_ml_is_defined.mdx +++ b/api_docs/kbn_ml_is_defined.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-defined title: "@kbn/ml-is-defined" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-defined plugin -date: 2023-02-23 +date: 2023-02-28 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 5d4903ce74f45..6670e94eb4eee 100644 --- a/api_docs/kbn_ml_is_populated_object.mdx +++ b/api_docs/kbn_ml_is_populated_object.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-populated-object title: "@kbn/ml-is-populated-object" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-populated-object plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-populated-object'] --- import kbnMlIsPopulatedObjectObj from './kbn_ml_is_populated_object.devdocs.json'; diff --git a/api_docs/kbn_ml_local_storage.mdx b/api_docs/kbn_ml_local_storage.mdx index e89cdec5269d1..bbd025fe1fa5a 100644 --- a/api_docs/kbn_ml_local_storage.mdx +++ b/api_docs/kbn_ml_local_storage.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-local-storage title: "@kbn/ml-local-storage" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-local-storage plugin -date: 2023-02-23 +date: 2023-02-28 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 73080cada19ef..c0554041a68a5 100644 --- a/api_docs/kbn_ml_nested_property.mdx +++ b/api_docs/kbn_ml_nested_property.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-nested-property title: "@kbn/ml-nested-property" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-nested-property plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-nested-property'] --- import kbnMlNestedPropertyObj from './kbn_ml_nested_property.devdocs.json'; diff --git a/api_docs/kbn_ml_query_utils.mdx b/api_docs/kbn_ml_query_utils.mdx index 5391158a73040..be947c303ff24 100644 --- a/api_docs/kbn_ml_query_utils.mdx +++ b/api_docs/kbn_ml_query_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-query-utils title: "@kbn/ml-query-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-query-utils plugin -date: 2023-02-23 +date: 2023-02-28 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_string_hash.mdx b/api_docs/kbn_ml_string_hash.mdx index a7af7d76facd1..12f3acbcf3dd9 100644 --- a/api_docs/kbn_ml_string_hash.mdx +++ b/api_docs/kbn_ml_string_hash.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-string-hash title: "@kbn/ml-string-hash" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-string-hash plugin -date: 2023-02-23 +date: 2023-02-28 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_url_state.mdx b/api_docs/kbn_ml_url_state.mdx index 4119bc0ae0c88..6a6b54987f4c0 100644 --- a/api_docs/kbn_ml_url_state.mdx +++ b/api_docs/kbn_ml_url_state.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-url-state title: "@kbn/ml-url-state" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-url-state plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-url-state'] --- import kbnMlUrlStateObj from './kbn_ml_url_state.devdocs.json'; diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index 09bc35b396439..78a9557f3a8e2 100644 --- a/api_docs/kbn_monaco.mdx +++ b/api_docs/kbn_monaco.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-monaco title: "@kbn/monaco" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/monaco plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] --- import kbnMonacoObj from './kbn_monaco.devdocs.json'; diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index be2a82a2a8caa..796787cdff114 100644 --- a/api_docs/kbn_optimizer.mdx +++ b/api_docs/kbn_optimizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer title: "@kbn/optimizer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer plugin -date: 2023-02-23 +date: 2023-02-28 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 8fc174a51929c..4c2ced69cd354 100644 --- a/api_docs/kbn_optimizer_webpack_helpers.mdx +++ b/api_docs/kbn_optimizer_webpack_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer-webpack-helpers title: "@kbn/optimizer-webpack-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer-webpack-helpers plugin -date: 2023-02-23 +date: 2023-02-28 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 d5b159b72c584..fcd4299d7dec6 100644 --- a/api_docs/kbn_osquery_io_ts_types.mdx +++ b/api_docs/kbn_osquery_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-osquery-io-ts-types title: "@kbn/osquery-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/osquery-io-ts-types plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/osquery-io-ts-types'] --- import kbnOsqueryIoTsTypesObj from './kbn_osquery_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index 40f7d77c5842a..8735a6ebf7771 100644 --- a/api_docs/kbn_performance_testing_dataset_extractor.mdx +++ b/api_docs/kbn_performance_testing_dataset_extractor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-performance-testing-dataset-extractor title: "@kbn/performance-testing-dataset-extractor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/performance-testing-dataset-extractor plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/performance-testing-dataset-extractor'] --- import kbnPerformanceTestingDatasetExtractorObj from './kbn_performance_testing_dataset_extractor.devdocs.json'; diff --git a/api_docs/kbn_plugin_generator.mdx b/api_docs/kbn_plugin_generator.mdx index 96f11ab16ca91..2fe0e395a926a 100644 --- a/api_docs/kbn_plugin_generator.mdx +++ b/api_docs/kbn_plugin_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-generator title: "@kbn/plugin-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-generator plugin -date: 2023-02-23 +date: 2023-02-28 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 6d8984f0d909b..b42e4ff694c8e 100644 --- a/api_docs/kbn_plugin_helpers.mdx +++ b/api_docs/kbn_plugin_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-helpers title: "@kbn/plugin-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-helpers plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx index 139966fba343c..26651728d35af 100644 --- a/api_docs/kbn_react_field.mdx +++ b/api_docs/kbn_react_field.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-field title: "@kbn/react-field" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-field plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_repo_file_maps.mdx b/api_docs/kbn_repo_file_maps.mdx index 02d6019b0de9d..97902ffe415b4 100644 --- a/api_docs/kbn_repo_file_maps.mdx +++ b/api_docs/kbn_repo_file_maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-file-maps title: "@kbn/repo-file-maps" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-file-maps plugin -date: 2023-02-23 +date: 2023-02-28 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 c37f2150dc3fd..90bce3b337ef4 100644 --- a/api_docs/kbn_repo_linter.mdx +++ b/api_docs/kbn_repo_linter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-linter title: "@kbn/repo-linter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-linter plugin -date: 2023-02-23 +date: 2023-02-28 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 ce68d25b70044..ea301fcb7bf65 100644 --- a/api_docs/kbn_repo_path.mdx +++ b/api_docs/kbn_repo_path.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-path title: "@kbn/repo-path" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-path plugin -date: 2023-02-23 +date: 2023-02-28 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 5f4e6710ae950..f027a5abaee44 100644 --- a/api_docs/kbn_repo_source_classifier.mdx +++ b/api_docs/kbn_repo_source_classifier.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-source-classifier title: "@kbn/repo-source-classifier" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-source-classifier plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-source-classifier'] --- import kbnRepoSourceClassifierObj from './kbn_repo_source_classifier.devdocs.json'; diff --git a/api_docs/kbn_rison.mdx b/api_docs/kbn_rison.mdx index 4da772e39cc5b..67b113801852c 100644 --- a/api_docs/kbn_rison.mdx +++ b/api_docs/kbn_rison.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rison title: "@kbn/rison" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rison plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rison'] --- import kbnRisonObj from './kbn_rison.devdocs.json'; diff --git a/api_docs/kbn_rule_data_utils.devdocs.json b/api_docs/kbn_rule_data_utils.devdocs.json index 51429bd28d4a4..0b4f41adabbdc 100644 --- a/api_docs/kbn_rule_data_utils.devdocs.json +++ b/api_docs/kbn_rule_data_utils.devdocs.json @@ -218,7 +218,7 @@ "signature": [ "\"kibana.alert.case_ids\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/default_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -300,13 +300,13 @@ }, { "parentPluginId": "@kbn/rule-data-utils", - "id": "def-common.ALERT_ID", + "id": "def-common.ALERT_FLAPPING_HISTORY", "type": "string", "tags": [], - "label": "ALERT_ID", + "label": "ALERT_FLAPPING_HISTORY", "description": [], "signature": [ - "\"kibana.alert.id\"" + "\"kibana.alert.flapping_history\"" ], "path": "packages/kbn-rule-data-utils/src/default_alerts_as_data.ts", "deprecated": false, @@ -323,7 +323,7 @@ "signature": [ "\"kibana.alert.instance.id\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/default_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -383,7 +383,7 @@ "signature": [ "\"kibana.alert.risk_score\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -398,7 +398,7 @@ "signature": [ "\"kibana.alert.rule.author\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -443,7 +443,7 @@ "signature": [ "\"kibana.alert.rule.created_at\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -458,7 +458,7 @@ "signature": [ "\"kibana.alert.rule.created_by\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -473,7 +473,7 @@ "signature": [ "\"kibana.alert.rule.description\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -488,7 +488,7 @@ "signature": [ "\"kibana.alert.rule.enabled\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -533,7 +533,7 @@ "signature": [ "\"kibana.alert.rule.from\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -548,7 +548,7 @@ "signature": [ "\"kibana.alert.rule.interval\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -563,7 +563,7 @@ "signature": [ "\"kibana.alert.rule.license\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -623,7 +623,7 @@ "signature": [ "\"kibana.alert.rule.note\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -668,7 +668,7 @@ "signature": [ "\"kibana.alert.rule.references\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -683,7 +683,7 @@ "signature": [ "\"kibana.alert.rule.rule_id\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -698,7 +698,7 @@ "signature": [ "\"kibana.alert.rule.rule_name_override\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -728,7 +728,7 @@ "signature": [ "\"kibana.alert.rule.to\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -743,7 +743,7 @@ "signature": [ "\"kibana.alert.rule.type\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -773,7 +773,7 @@ "signature": [ "\"kibana.alert.rule.updated_at\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -788,7 +788,7 @@ "signature": [ "\"kibana.alert.rule.updated_by\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -818,7 +818,7 @@ "signature": [ "\"kibana.alert.rule.version\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -833,7 +833,7 @@ "signature": [ "\"kibana.alert.severity\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -938,7 +938,7 @@ "signature": [ "\"kibana.alert.suppression.docs_count\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -953,7 +953,7 @@ "signature": [ "\"kibana.alert.suppression.end\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -968,7 +968,7 @@ "signature": [ "\"kibana.alert.suppression.terms.field\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -983,7 +983,7 @@ "signature": [ "\"kibana.alert.suppression.start\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -998,7 +998,7 @@ "signature": [ "\"kibana.alert.suppression.terms\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -1013,7 +1013,7 @@ "signature": [ "\"kibana.alert.suppression.terms.value\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -1028,7 +1028,7 @@ "signature": [ "\"kibana.alert.system_status\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -1223,7 +1223,7 @@ "signature": [ "\"kibana.alert.workflow_reason\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -1253,7 +1253,7 @@ "signature": [ "\"kibana.alert.workflow_user\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -1311,7 +1311,7 @@ "label": "DefaultAlertFieldName", "description": [], "signature": [ - "\"kibana\" | \"kibana.alert.rule.rule_type_id\" | \"kibana.alert.rule.consumer\" | \"kibana.alert.rule.execution.uuid\" | \"kibana.alert\" | \"kibana.alert.rule\" | \"kibana.alert.rule.parameters\" | \"kibana.alert.rule.producer\" | \"kibana.space_ids\" | \"kibana.alert.uuid\" | \"kibana.alert.start\" | \"kibana.alert.time_range\" | \"kibana.alert.end\" | \"kibana.alert.duration.us\" | \"kibana.alert.status\" | \"kibana.alert.flapping\" | \"kibana.version\" | \"kibana.alert.workflow_status\" | \"kibana.alert.action_group\" | \"kibana.alert.reason\" | \"kibana.alert.rule.category\" | \"kibana.alert.rule.uuid\" | \"kibana.alert.rule.name\" | \"kibana.alert.rule.tags\" | \"kibana.alert.last_detected\" | \"kibana.alert.id\"" + "\"@timestamp\" | \"kibana\" | \"kibana.alert.rule.rule_type_id\" | \"kibana.alert.rule.consumer\" | \"kibana.alert.rule.execution.uuid\" | \"kibana.alert\" | \"kibana.alert.action_group\" | \"kibana.alert.case_ids\" | \"kibana.alert.duration.us\" | \"kibana.alert.end\" | \"kibana.alert.flapping\" | \"kibana.alert.flapping_history\" | \"kibana.alert.instance.id\" | \"kibana.alert.last_detected\" | \"kibana.alert.reason\" | \"kibana.alert.rule\" | \"kibana.alert.rule.category\" | \"kibana.alert.rule.name\" | \"kibana.alert.rule.parameters\" | \"kibana.alert.rule.producer\" | \"kibana.alert.rule.tags\" | \"kibana.alert.rule.uuid\" | \"kibana.alert.start\" | \"kibana.alert.status\" | \"kibana.alert.time_range\" | \"kibana.alert.uuid\" | \"kibana.alert.workflow_status\" | \"kibana.space_ids\" | \"kibana.version\"" ], "path": "packages/kbn-rule-data-utils/src/default_alerts_as_data.ts", "deprecated": false, @@ -1328,7 +1328,7 @@ "signature": [ "\"ecs.version\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -1343,7 +1343,7 @@ "signature": [ "\"event.action\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -1358,7 +1358,7 @@ "signature": [ "\"event.kind\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -1463,7 +1463,7 @@ "signature": [ "\"tags\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -1476,7 +1476,7 @@ "label": "TechnicalRuleDataFieldName", "description": [], "signature": [ - "\"@timestamp\" | \"event.action\" | \"tags\" | \"kibana\" | \"kibana.alert.rule.rule_type_id\" | \"kibana.alert.rule.consumer\" | \"kibana.alert.rule.execution.uuid\" | \"kibana.alert\" | \"kibana.alert.rule\" | \"kibana.alert.rule.parameters\" | \"kibana.alert.rule.producer\" | \"kibana.space_ids\" | \"kibana.alert.uuid\" | \"kibana.alert.instance.id\" | \"kibana.alert.start\" | \"kibana.alert.time_range\" | \"kibana.alert.end\" | \"kibana.alert.duration.us\" | \"kibana.alert.severity\" | \"kibana.alert.status\" | \"kibana.alert.flapping\" | \"kibana.version\" | \"ecs.version\" | \"kibana.alert.risk_score\" | \"kibana.alert.workflow_status\" | \"kibana.alert.workflow_user\" | \"kibana.alert.workflow_reason\" | \"kibana.alert.system_status\" | \"kibana.alert.action_group\" | \"kibana.alert.reason\" | \"kibana.alert.case_ids\" | \"kibana.alert.rule.author\" | \"kibana.alert.rule.category\" | \"kibana.alert.rule.uuid\" | \"kibana.alert.rule.created_at\" | \"kibana.alert.rule.created_by\" | \"kibana.alert.rule.description\" | \"kibana.alert.rule.enabled\" | \"kibana.alert.rule.from\" | \"kibana.alert.rule.interval\" | \"kibana.alert.rule.license\" | \"kibana.alert.rule.name\" | \"kibana.alert.rule.note\" | \"kibana.alert.rule.references\" | \"kibana.alert.rule.rule_id\" | \"kibana.alert.rule.rule_name_override\" | \"kibana.alert.rule.tags\" | \"kibana.alert.rule.to\" | \"kibana.alert.rule.type\" | \"kibana.alert.rule.updated_at\" | \"kibana.alert.rule.updated_by\" | \"kibana.alert.rule.version\" | \"kibana.alert.suppression.terms\" | \"kibana.alert.suppression.terms.field\" | \"kibana.alert.suppression.terms.value\" | \"kibana.alert.suppression.start\" | \"kibana.alert.suppression.end\" | \"kibana.alert.suppression.docs_count\" | \"event.kind\" | \"event.module\" | \"kibana.alert.evaluation.threshold\" | \"kibana.alert.evaluation.value\" | \"kibana.alert.building_block_type\" | \"kibana.alert.rule.exceptions_list\" | \"kibana.alert.rule.namespace\" | \"kibana.alert.rule.threat.framework\" | \"kibana.alert.rule.threat.tactic.id\" | \"kibana.alert.rule.threat.tactic.name\" | \"kibana.alert.rule.threat.tactic.reference\" | \"kibana.alert.rule.threat.technique.id\" | \"kibana.alert.rule.threat.technique.name\" | \"kibana.alert.rule.threat.technique.reference\" | \"kibana.alert.rule.threat.technique.subtechnique.id\" | \"kibana.alert.rule.threat.technique.subtechnique.name\" | \"kibana.alert.rule.threat.technique.subtechnique.reference\"" + "\"@timestamp\" | \"event.action\" | \"tags\" | \"kibana\" | \"kibana.alert.rule.rule_type_id\" | \"kibana.alert.rule.consumer\" | \"kibana.alert.rule.execution.uuid\" | \"kibana.alert\" | \"kibana.alert.action_group\" | \"kibana.alert.case_ids\" | \"kibana.alert.duration.us\" | \"kibana.alert.end\" | \"kibana.alert.flapping\" | \"kibana.alert.instance.id\" | \"kibana.alert.reason\" | \"kibana.alert.rule\" | \"kibana.alert.rule.category\" | \"kibana.alert.rule.name\" | \"kibana.alert.rule.parameters\" | \"kibana.alert.rule.producer\" | \"kibana.alert.rule.tags\" | \"kibana.alert.rule.uuid\" | \"kibana.alert.start\" | \"kibana.alert.status\" | \"kibana.alert.time_range\" | \"kibana.alert.uuid\" | \"kibana.alert.workflow_status\" | \"kibana.space_ids\" | \"kibana.version\" | \"kibana.alert.risk_score\" | \"kibana.alert.rule.author\" | \"kibana.alert.rule.created_at\" | \"kibana.alert.rule.created_by\" | \"kibana.alert.rule.description\" | \"kibana.alert.rule.enabled\" | \"kibana.alert.rule.from\" | \"kibana.alert.rule.interval\" | \"kibana.alert.rule.license\" | \"kibana.alert.rule.note\" | \"kibana.alert.rule.references\" | \"kibana.alert.rule.rule_id\" | \"kibana.alert.rule.rule_name_override\" | \"kibana.alert.rule.to\" | \"kibana.alert.rule.type\" | \"kibana.alert.rule.updated_at\" | \"kibana.alert.rule.updated_by\" | \"kibana.alert.rule.version\" | \"kibana.alert.severity\" | \"kibana.alert.suppression.docs_count\" | \"kibana.alert.suppression.end\" | \"kibana.alert.suppression.terms\" | \"kibana.alert.suppression.terms.field\" | \"kibana.alert.suppression.start\" | \"kibana.alert.suppression.terms.value\" | \"kibana.alert.system_status\" | \"kibana.alert.workflow_reason\" | \"kibana.alert.workflow_user\" | \"ecs.version\" | \"event.kind\" | \"kibana.alert.evaluation.threshold\" | \"kibana.alert.evaluation.value\" | \"event.module\" | \"kibana.alert.building_block_type\" | \"kibana.alert.rule.exceptions_list\" | \"kibana.alert.rule.namespace\" | \"kibana.alert.rule.threat.framework\" | \"kibana.alert.rule.threat.tactic.id\" | \"kibana.alert.rule.threat.tactic.name\" | \"kibana.alert.rule.threat.tactic.reference\" | \"kibana.alert.rule.threat.technique.id\" | \"kibana.alert.rule.threat.technique.name\" | \"kibana.alert.rule.threat.technique.reference\" | \"kibana.alert.rule.threat.technique.subtechnique.id\" | \"kibana.alert.rule.threat.technique.subtechnique.name\" | \"kibana.alert.rule.threat.technique.subtechnique.reference\"" ], "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", "deprecated": false, @@ -1493,7 +1493,7 @@ "signature": [ "\"@timestamp\"" ], - "path": "packages/kbn-rule-data-utils/src/technical_field_names.ts", + "path": "packages/kbn-rule-data-utils/src/default_alerts_as_data.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index ff9810f12bf52..314a0e7da1a48 100644 --- a/api_docs/kbn_rule_data_utils.mdx +++ b/api_docs/kbn_rule_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rule-data-utils title: "@kbn/rule-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rule-data-utils plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils'] --- import kbnRuleDataUtilsObj from './kbn_rule_data_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index c9bfbafffd33b..5a8bc014bed9c 100644 --- a/api_docs/kbn_securitysolution_autocomplete.mdx +++ b/api_docs/kbn_securitysolution_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-autocomplete title: "@kbn/securitysolution-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-autocomplete plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-autocomplete'] --- import kbnSecuritysolutionAutocompleteObj from './kbn_securitysolution_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_ecs.mdx b/api_docs/kbn_securitysolution_ecs.mdx index 4b3375a8aff55..5ee5d919296b0 100644 --- a/api_docs/kbn_securitysolution_ecs.mdx +++ b/api_docs/kbn_securitysolution_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-ecs title: "@kbn/securitysolution-ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-ecs plugin -date: 2023-02-23 +date: 2023-02-28 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 19a4b4ac5fcae..bd64b1db81bfc 100644 --- a/api_docs/kbn_securitysolution_es_utils.mdx +++ b/api_docs/kbn_securitysolution_es_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-es-utils title: "@kbn/securitysolution-es-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-es-utils plugin -date: 2023-02-23 +date: 2023-02-28 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 fd947a543a965..85aceb9690e36 100644 --- a/api_docs/kbn_securitysolution_exception_list_components.mdx +++ b/api_docs/kbn_securitysolution_exception_list_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-exception-list-components title: "@kbn/securitysolution-exception-list-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-exception-list-components plugin -date: 2023-02-23 +date: 2023-02-28 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_hook_utils.mdx b/api_docs/kbn_securitysolution_hook_utils.mdx index c6e556c306478..5bc067d5e6340 100644 --- a/api_docs/kbn_securitysolution_hook_utils.mdx +++ b/api_docs/kbn_securitysolution_hook_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-hook-utils title: "@kbn/securitysolution-hook-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-hook-utils plugin -date: 2023-02-23 +date: 2023-02-28 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 ab818ca6f83b5..9864d4070a64f 100644 --- a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-alerting-types title: "@kbn/securitysolution-io-ts-alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-alerting-types plugin -date: 2023-02-23 +date: 2023-02-28 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 6e5ee0fdee989..f2095245ff1ac 100644 --- a/api_docs/kbn_securitysolution_io_ts_list_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_list_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-list-types title: "@kbn/securitysolution-io-ts-list-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-list-types plugin -date: 2023-02-23 +date: 2023-02-28 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 757705d927822..5668e299d77f0 100644 --- a/api_docs/kbn_securitysolution_io_ts_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-types title: "@kbn/securitysolution-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-types plugin -date: 2023-02-23 +date: 2023-02-28 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 34a2d415c76a3..d8d0c9c9871d8 100644 --- a/api_docs/kbn_securitysolution_io_ts_utils.mdx +++ b/api_docs/kbn_securitysolution_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-utils title: "@kbn/securitysolution-io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-utils plugin -date: 2023-02-23 +date: 2023-02-28 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 4c21c7841c3b7..82a67efe6b38a 100644 --- a/api_docs/kbn_securitysolution_list_api.mdx +++ b/api_docs/kbn_securitysolution_list_api.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-api title: "@kbn/securitysolution-list-api" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-api plugin -date: 2023-02-23 +date: 2023-02-28 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 cde1ad80cc19a..e0f50cd3efed4 100644 --- a/api_docs/kbn_securitysolution_list_constants.mdx +++ b/api_docs/kbn_securitysolution_list_constants.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-constants title: "@kbn/securitysolution-list-constants" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-constants plugin -date: 2023-02-23 +date: 2023-02-28 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 5f0b9b3a06d8b..cf55a63421c8d 100644 --- a/api_docs/kbn_securitysolution_list_hooks.mdx +++ b/api_docs/kbn_securitysolution_list_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-hooks title: "@kbn/securitysolution-list-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-hooks plugin -date: 2023-02-23 +date: 2023-02-28 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 dab8027d5ff1b..73199675135f7 100644 --- a/api_docs/kbn_securitysolution_list_utils.mdx +++ b/api_docs/kbn_securitysolution_list_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-utils title: "@kbn/securitysolution-list-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-utils plugin -date: 2023-02-23 +date: 2023-02-28 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 40e842c53c72c..d8cc6bdea546d 100644 --- a/api_docs/kbn_securitysolution_rules.mdx +++ b/api_docs/kbn_securitysolution_rules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-rules title: "@kbn/securitysolution-rules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-rules plugin -date: 2023-02-23 +date: 2023-02-28 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 83a605cec5db8..be04da8990108 100644 --- a/api_docs/kbn_securitysolution_t_grid.mdx +++ b/api_docs/kbn_securitysolution_t_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-t-grid title: "@kbn/securitysolution-t-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-t-grid plugin -date: 2023-02-23 +date: 2023-02-28 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 1f2950397ff0b..b870995270f14 100644 --- a/api_docs/kbn_securitysolution_utils.mdx +++ b/api_docs/kbn_securitysolution_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-utils title: "@kbn/securitysolution-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-utils plugin -date: 2023-02-23 +date: 2023-02-28 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 f2efcfef5d28f..e2f2fa7144b85 100644 --- a/api_docs/kbn_server_http_tools.mdx +++ b/api_docs/kbn_server_http_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-http-tools title: "@kbn/server-http-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-http-tools plugin -date: 2023-02-23 +date: 2023-02-28 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 625bf5e0c0910..733dda4175166 100644 --- a/api_docs/kbn_server_route_repository.mdx +++ b/api_docs/kbn_server_route_repository.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository title: "@kbn/server-route-repository" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-route-repository plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_shared_svg.mdx b/api_docs/kbn_shared_svg.mdx index 3f2ac85168d3b..e58c594bf32bf 100644 --- a/api_docs/kbn_shared_svg.mdx +++ b/api_docs/kbn_shared_svg.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-svg title: "@kbn/shared-svg" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-svg plugin -date: 2023-02-23 +date: 2023-02-28 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 d3ce2ec85cbdd..2269ceb340140 100644 --- a/api_docs/kbn_shared_ux_avatar_solution.mdx +++ b/api_docs/kbn_shared_ux_avatar_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-solution title: "@kbn/shared-ux-avatar-solution" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-solution plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-solution'] --- import kbnSharedUxAvatarSolutionObj from './kbn_shared_ux_avatar_solution.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx index 61f2189c2026a..8e47740cb12e6 100644 --- a/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx +++ b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-user-profile-components title: "@kbn/shared-ux-avatar-user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-user-profile-components plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-user-profile-components'] --- import kbnSharedUxAvatarUserProfileComponentsObj from './kbn_shared_ux_avatar_user_profile_components.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx index d02f04679dda9..07e493a7cb81a 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen title: "@kbn/shared-ux-button-exit-full-screen" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen'] --- import kbnSharedUxButtonExitFullScreenObj from './kbn_shared_ux_button_exit_full_screen.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx index e71fe413f423e..0c681b7541cb9 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen-mocks title: "@kbn/shared-ux-button-exit-full-screen-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen-mocks plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen-mocks'] --- import kbnSharedUxButtonExitFullScreenMocksObj from './kbn_shared_ux_button_exit_full_screen_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_toolbar.devdocs.json b/api_docs/kbn_shared_ux_button_toolbar.devdocs.json index 67ad616db880e..57ab85b6c5e8e 100644 --- a/api_docs/kbn_shared_ux_button_toolbar.devdocs.json +++ b/api_docs/kbn_shared_ux_button_toolbar.devdocs.json @@ -119,15 +119,15 @@ }, { "parentPluginId": "@kbn/shared-ux-button-toolbar", - "id": "def-common.PrimaryButton", + "id": "def-common.Toolbar", "type": "Function", "tags": [], - "label": "PrimaryButton", + "label": "Toolbar", "description": [ - "\nA primary action button, usually appearing first in the toolbar." + "\n" ], "signature": [ - "({ label, iconSide, ...rest }: ", + "({ children }: ", { "pluginId": "@kbn/shared-ux-button-toolbar", "scope": "common", @@ -137,16 +137,16 @@ }, ") => JSX.Element" ], - "path": "packages/shared-ux/button_toolbar/src/buttons/primary/primary.tsx", + "path": "packages/shared-ux/button_toolbar/src/toolbar/toolbar.tsx", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/shared-ux-button-toolbar", - "id": "def-common.PrimaryButton.$1", + "id": "def-common.Toolbar.$1", "type": "Object", "tags": [], - "label": "{ label, iconSide = 'left', ...rest }", + "label": "{ children }", "description": [], "signature": [ { @@ -157,26 +157,26 @@ "text": "Props" } ], - "path": "packages/shared-ux/button_toolbar/src/buttons/primary/primary.tsx", + "path": "packages/shared-ux/button_toolbar/src/toolbar/toolbar.tsx", "deprecated": false, "trackAdoption": false, "isRequired": true } ], - "returnComment": [], + "returnComment": [ + "Toolbar component" + ], "initialIsOpen": false }, { "parentPluginId": "@kbn/shared-ux-button-toolbar", - "id": "def-common.Toolbar", + "id": "def-common.ToolbarButton", "type": "Function", "tags": [], - "label": "Toolbar", - "description": [ - "\n" - ], + "label": "ToolbarButton", + "description": [], "signature": [ - "({ children }: ", + "({ label, type, iconSide, ...rest }: React.PropsWithChildren<", { "pluginId": "@kbn/shared-ux-button-toolbar", "scope": "common", @@ -184,37 +184,37 @@ "section": "def-common.Props", "text": "Props" }, - ") => JSX.Element" + ">) => JSX.Element" ], - "path": "packages/shared-ux/button_toolbar/src/toolbar/toolbar.tsx", + "path": "packages/shared-ux/button_toolbar/src/buttons/toolbar_button/toolbar_button.tsx", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/shared-ux-button-toolbar", - "id": "def-common.Toolbar.$1", - "type": "Object", + "id": "def-common.ToolbarButton.$1", + "type": "CompoundType", "tags": [], - "label": "{ children }", + "label": "{\n label,\n type = 'empty',\n iconSide = 'left',\n ...rest\n}", "description": [], "signature": [ + "React.PropsWithChildren<", { "pluginId": "@kbn/shared-ux-button-toolbar", "scope": "common", "docId": "kibKbnSharedUxButtonToolbarPluginApi", "section": "def-common.Props", "text": "Props" - } + }, + ">" ], - "path": "packages/shared-ux/button_toolbar/src/toolbar/toolbar.tsx", + "path": "packages/shared-ux/button_toolbar/src/buttons/toolbar_button/toolbar_button.tsx", "deprecated": false, "trackAdoption": false, "isRequired": true } ], - "returnComment": [ - "Toolbar component" - ], + "returnComment": [], "initialIsOpen": false }, { @@ -227,7 +227,7 @@ "\nA button which opens a popover of additional actions within the toolbar." ], "signature": [ - "({ label, iconType, children, iconSide, ...popover }: ", + "({ type, label, iconType, children, ...popover }: ", { "pluginId": "@kbn/shared-ux-button-toolbar", "scope": "common", @@ -246,7 +246,7 @@ "id": "def-common.ToolbarPopover.$1", "type": "CompoundType", "tags": [], - "label": "{ label, iconType, children, iconSide, ...popover }", + "label": "{ type, label, iconType, children, ...popover }", "description": [], "signature": [ { @@ -343,6 +343,22 @@ "path": "packages/shared-ux/button_toolbar/src/buttons/icon_button_group/icon_button_group.tsx", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "@kbn/shared-ux-button-toolbar", + "id": "def-common.IconButton.datatestsubj", + "type": "string", + "tags": [], + "label": "'data-test-subj'", + "description": [ + "Test subject for button" + ], + "signature": [ + "string | undefined" + ], + "path": "packages/shared-ux/button_toolbar/src/buttons/icon_button_group/icon_button_group.tsx", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -418,9 +434,9 @@ }, " extends Pick<", "EuiButtonPropsForButton", - ", \"onClick\" | \"iconType\" | \"iconSide\">" + ", \"data-test-subj\" | \"onClick\" | \"iconType\" | \"iconSide\">" ], - "path": "packages/shared-ux/button_toolbar/src/buttons/primary/primary.tsx", + "path": "packages/shared-ux/button_toolbar/src/buttons/toolbar_button/toolbar_button.tsx", "deprecated": false, "trackAdoption": false, "children": [ @@ -431,7 +447,21 @@ "tags": [], "label": "label", "description": [], - "path": "packages/shared-ux/button_toolbar/src/buttons/primary/primary.tsx", + "path": "packages/shared-ux/button_toolbar/src/buttons/toolbar_button/toolbar_button.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/shared-ux-button-toolbar", + "id": "def-common.Props.type", + "type": "CompoundType", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "ToolbarButtonTypes | undefined" + ], + "path": "packages/shared-ux/button_toolbar/src/buttons/toolbar_button/toolbar_button.tsx", "deprecated": false, "trackAdoption": false } @@ -479,7 +509,7 @@ "label": "Props", "description": [], "signature": [ - "{ onClick?: React.MouseEventHandler | undefined; iconSide?: ", + "{ 'data-test-subj'?: string | undefined; onClick?: React.MouseEventHandler | undefined; iconSide?: ", "ButtonContentIconSide", "; }" ], @@ -507,15 +537,15 @@ }, { "parentPluginId": "@kbn/shared-ux-button-toolbar", - "id": "def-common.ToolbarButton", + "id": "def-common.ToolbarButtonType", "type": "Type", "tags": [], - "label": "ToolbarButton", + "label": "ToolbarButtonType", "description": [ "type for cases with both button or a popover could be used" ], "signature": [ - "(({ label, iconSide, ...rest }: ", + "React.FunctionComponent<", { "pluginId": "@kbn/shared-ux-button-toolbar", "scope": "common", @@ -523,7 +553,7 @@ "section": "def-common.Props", "text": "Props" }, - ") => JSX.Element) | (({ label, iconType, children, iconSide, ...popover }: ", + "> | (({ type, label, iconType, children, ...popover }: ", { "pluginId": "@kbn/shared-ux-button-toolbar", "scope": "common", diff --git a/api_docs/kbn_shared_ux_button_toolbar.mdx b/api_docs/kbn_shared_ux_button_toolbar.mdx index 2737f7e898633..606d8505db64b 100644 --- a/api_docs/kbn_shared_ux_button_toolbar.mdx +++ b/api_docs/kbn_shared_ux_button_toolbar.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-toolbar title: "@kbn/shared-ux-button-toolbar" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-toolbar plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-toolbar'] --- import kbnSharedUxButtonToolbarObj from './kbn_shared_ux_button_toolbar.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sh | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 25 | 0 | 8 | 0 | +| 27 | 0 | 10 | 0 | ## Common diff --git a/api_docs/kbn_shared_ux_card_no_data.devdocs.json b/api_docs/kbn_shared_ux_card_no_data.devdocs.json index 54defc33a74eb..8c7dec127aee0 100644 --- a/api_docs/kbn_shared_ux_card_no_data.devdocs.json +++ b/api_docs/kbn_shared_ux_card_no_data.devdocs.json @@ -188,7 +188,7 @@ "Interpolation", "<", "Theme", - ">; defaultChecked?: boolean | undefined; suppressContentEditableWarning?: boolean | undefined; suppressHydrationWarning?: boolean | undefined; accessKey?: string | undefined; contentEditable?: Booleanish | \"inherit\" | undefined; contextMenu?: string | undefined; dir?: string | undefined; draggable?: Booleanish | undefined; hidden?: boolean | undefined; lang?: string | undefined; placeholder?: string | undefined; spellCheck?: Booleanish | undefined; tabIndex?: number | undefined; translate?: \"yes\" | \"no\" | undefined; radioGroup?: string | undefined; role?: React.AriaRole | undefined; about?: string | undefined; datatype?: string | undefined; inlist?: any; property?: string | undefined; resource?: string | undefined; typeof?: string | undefined; vocab?: string | undefined; autoCapitalize?: string | undefined; autoCorrect?: string | undefined; autoSave?: string | undefined; itemProp?: string | undefined; itemScope?: boolean | undefined; itemType?: string | undefined; itemID?: string | undefined; itemRef?: string | undefined; results?: number | undefined; security?: string | undefined; unselectable?: \"on\" | \"off\" | undefined; inputMode?: \"search\" | \"none\" | \"text\" | \"tel\" | \"url\" | \"email\" | \"numeric\" | \"decimal\" | undefined; 'aria-activedescendant'?: string | undefined; 'aria-atomic'?: Booleanish | undefined; 'aria-autocomplete'?: \"none\" | \"list\" | \"inline\" | \"both\" | undefined; 'aria-busy'?: Booleanish | undefined; 'aria-checked'?: boolean | \"true\" | \"false\" | \"mixed\" | undefined; 'aria-colcount'?: number | undefined; 'aria-colindex'?: number | undefined; 'aria-colspan'?: number | undefined; 'aria-controls'?: string | undefined; 'aria-current'?: boolean | \"page\" | \"date\" | \"time\" | \"true\" | \"false\" | \"step\" | \"location\" | undefined; 'aria-describedby'?: string | undefined; 'aria-details'?: string | undefined; 'aria-disabled'?: Booleanish | undefined; 'aria-dropeffect'?: \"execute\" | \"link\" | \"none\" | \"copy\" | \"move\" | \"popup\" | undefined; 'aria-errormessage'?: string | undefined; 'aria-expanded'?: Booleanish | undefined; 'aria-flowto'?: string | undefined; 'aria-grabbed'?: Booleanish | undefined; 'aria-haspopup'?: boolean | \"dialog\" | \"menu\" | \"true\" | \"false\" | \"grid\" | \"listbox\" | \"tree\" | undefined; 'aria-hidden'?: Booleanish | undefined; 'aria-invalid'?: boolean | \"true\" | \"false\" | \"grammar\" | \"spelling\" | undefined; 'aria-keyshortcuts'?: string | undefined; 'aria-labelledby'?: string | undefined; 'aria-level'?: number | undefined; 'aria-live'?: \"off\" | \"assertive\" | \"polite\" | undefined; 'aria-modal'?: Booleanish | undefined; 'aria-multiline'?: Booleanish | undefined; 'aria-multiselectable'?: Booleanish | undefined; 'aria-orientation'?: \"horizontal\" | \"vertical\" | undefined; 'aria-owns'?: string | undefined; 'aria-placeholder'?: string | undefined; 'aria-posinset'?: number | undefined; 'aria-pressed'?: boolean | \"true\" | \"false\" | \"mixed\" | undefined; 'aria-readonly'?: Booleanish | undefined; 'aria-relevant'?: \"text\" | \"additions\" | \"additions removals\" | \"additions text\" | \"all\" | \"removals\" | \"removals additions\" | \"removals text\" | \"text additions\" | \"text removals\" | undefined; 'aria-required'?: Booleanish | undefined; 'aria-roledescription'?: string | undefined; 'aria-rowcount'?: number | undefined; 'aria-rowindex'?: number | undefined; 'aria-rowspan'?: number | undefined; 'aria-selected'?: Booleanish | undefined; 'aria-setsize'?: number | undefined; 'aria-sort'?: \"none\" | \"ascending\" | \"descending\" | \"other\" | undefined; 'aria-valuemax'?: number | undefined; 'aria-valuemin'?: number | undefined; 'aria-valuenow'?: number | undefined; 'aria-valuetext'?: string | undefined; dangerouslySetInnerHTML?: { __html: string; } | undefined; onCopy?: React.ClipboardEventHandler | undefined; onCopyCapture?: React.ClipboardEventHandler | undefined; onCut?: React.ClipboardEventHandler | undefined; onCutCapture?: React.ClipboardEventHandler | undefined; onPaste?: React.ClipboardEventHandler | undefined; onPasteCapture?: React.ClipboardEventHandler | undefined; onCompositionEnd?: React.CompositionEventHandler | undefined; onCompositionEndCapture?: React.CompositionEventHandler | undefined; onCompositionStart?: React.CompositionEventHandler | undefined; onCompositionStartCapture?: React.CompositionEventHandler | undefined; onCompositionUpdate?: React.CompositionEventHandler | undefined; onCompositionUpdateCapture?: React.CompositionEventHandler | undefined; onFocus?: React.FocusEventHandler | undefined; onFocusCapture?: React.FocusEventHandler | undefined; onBlur?: React.FocusEventHandler | undefined; onBlurCapture?: React.FocusEventHandler | undefined; onChange?: React.FormEventHandler | undefined; onChangeCapture?: React.FormEventHandler | undefined; onBeforeInput?: React.FormEventHandler | undefined; onBeforeInputCapture?: React.FormEventHandler | undefined; onInput?: React.FormEventHandler | undefined; onInputCapture?: React.FormEventHandler | undefined; onReset?: React.FormEventHandler | undefined; onResetCapture?: React.FormEventHandler | undefined; onSubmit?: React.FormEventHandler | undefined; onSubmitCapture?: React.FormEventHandler | undefined; onInvalid?: React.FormEventHandler | undefined; onInvalidCapture?: React.FormEventHandler | undefined; onLoad?: React.ReactEventHandler | undefined; onLoadCapture?: React.ReactEventHandler | undefined; onError?: React.ReactEventHandler | undefined; onErrorCapture?: React.ReactEventHandler | undefined; onKeyDown?: React.KeyboardEventHandler | undefined; onKeyDownCapture?: React.KeyboardEventHandler | undefined; onKeyPress?: React.KeyboardEventHandler | undefined; onKeyPressCapture?: React.KeyboardEventHandler | undefined; onKeyUp?: React.KeyboardEventHandler | undefined; onKeyUpCapture?: React.KeyboardEventHandler | undefined; onAbort?: React.ReactEventHandler | undefined; onAbortCapture?: React.ReactEventHandler | undefined; onCanPlay?: React.ReactEventHandler | undefined; onCanPlayCapture?: React.ReactEventHandler | undefined; onCanPlayThrough?: React.ReactEventHandler | undefined; onCanPlayThroughCapture?: React.ReactEventHandler | undefined; onDurationChange?: React.ReactEventHandler | undefined; onDurationChangeCapture?: React.ReactEventHandler | undefined; onEmptied?: React.ReactEventHandler | undefined; onEmptiedCapture?: React.ReactEventHandler | undefined; onEncrypted?: React.ReactEventHandler | undefined; onEncryptedCapture?: React.ReactEventHandler | undefined; onEnded?: React.ReactEventHandler | undefined; onEndedCapture?: React.ReactEventHandler | undefined; onLoadedData?: React.ReactEventHandler | undefined; onLoadedDataCapture?: React.ReactEventHandler | undefined; onLoadedMetadata?: React.ReactEventHandler | undefined; onLoadedMetadataCapture?: React.ReactEventHandler | undefined; onLoadStart?: React.ReactEventHandler | undefined; onLoadStartCapture?: React.ReactEventHandler | undefined; onPause?: React.ReactEventHandler | undefined; onPauseCapture?: React.ReactEventHandler | undefined; onPlay?: React.ReactEventHandler | undefined; onPlayCapture?: React.ReactEventHandler | undefined; onPlaying?: React.ReactEventHandler | undefined; onPlayingCapture?: React.ReactEventHandler | undefined; onProgress?: React.ReactEventHandler | undefined; onProgressCapture?: React.ReactEventHandler | undefined; onRateChange?: React.ReactEventHandler | undefined; onRateChangeCapture?: React.ReactEventHandler | undefined; onSeeked?: React.ReactEventHandler | undefined; onSeekedCapture?: React.ReactEventHandler | undefined; onSeeking?: React.ReactEventHandler | undefined; onSeekingCapture?: React.ReactEventHandler | undefined; onStalled?: React.ReactEventHandler | undefined; onStalledCapture?: React.ReactEventHandler | undefined; onSuspend?: React.ReactEventHandler | undefined; onSuspendCapture?: React.ReactEventHandler | undefined; onTimeUpdate?: React.ReactEventHandler | undefined; onTimeUpdateCapture?: React.ReactEventHandler | undefined; onVolumeChange?: React.ReactEventHandler | undefined; onVolumeChangeCapture?: React.ReactEventHandler | undefined; onWaiting?: React.ReactEventHandler | undefined; onWaitingCapture?: React.ReactEventHandler | undefined; onAuxClick?: React.MouseEventHandler | undefined; onAuxClickCapture?: React.MouseEventHandler | undefined; onClick?: React.MouseEventHandler | undefined; onClickCapture?: React.MouseEventHandler | undefined; onContextMenu?: React.MouseEventHandler | undefined; onContextMenuCapture?: React.MouseEventHandler | undefined; onDoubleClick?: React.MouseEventHandler | undefined; onDoubleClickCapture?: React.MouseEventHandler | undefined; onDrag?: React.DragEventHandler | undefined; onDragCapture?: React.DragEventHandler | undefined; onDragEnd?: React.DragEventHandler | undefined; onDragEndCapture?: React.DragEventHandler | undefined; onDragEnter?: React.DragEventHandler | undefined; onDragEnterCapture?: React.DragEventHandler | undefined; onDragExit?: React.DragEventHandler | undefined; onDragExitCapture?: React.DragEventHandler | undefined; onDragLeave?: React.DragEventHandler | undefined; onDragLeaveCapture?: React.DragEventHandler | undefined; onDragOver?: React.DragEventHandler | undefined; onDragOverCapture?: React.DragEventHandler | undefined; onDragStart?: React.DragEventHandler | undefined; onDragStartCapture?: React.DragEventHandler | undefined; onDrop?: React.DragEventHandler | undefined; onDropCapture?: React.DragEventHandler | undefined; onMouseDown?: React.MouseEventHandler | undefined; onMouseDownCapture?: React.MouseEventHandler | undefined; onMouseEnter?: React.MouseEventHandler | undefined; onMouseLeave?: React.MouseEventHandler | undefined; onMouseMove?: React.MouseEventHandler | undefined; onMouseMoveCapture?: React.MouseEventHandler | undefined; onMouseOut?: React.MouseEventHandler | undefined; onMouseOutCapture?: React.MouseEventHandler | undefined; onMouseOver?: React.MouseEventHandler | undefined; onMouseOverCapture?: React.MouseEventHandler | undefined; onMouseUp?: React.MouseEventHandler | undefined; onMouseUpCapture?: React.MouseEventHandler | undefined; onSelect?: React.ReactEventHandler | undefined; onSelectCapture?: React.ReactEventHandler | undefined; onTouchCancel?: React.TouchEventHandler | undefined; onTouchCancelCapture?: React.TouchEventHandler | undefined; onTouchEnd?: React.TouchEventHandler | undefined; onTouchEndCapture?: React.TouchEventHandler | undefined; onTouchMove?: React.TouchEventHandler | undefined; onTouchMoveCapture?: React.TouchEventHandler | undefined; onTouchStart?: React.TouchEventHandler | undefined; onTouchStartCapture?: React.TouchEventHandler | undefined; onPointerDown?: React.PointerEventHandler | undefined; onPointerDownCapture?: React.PointerEventHandler | undefined; onPointerMove?: React.PointerEventHandler | undefined; onPointerMoveCapture?: React.PointerEventHandler | undefined; onPointerUp?: React.PointerEventHandler | undefined; onPointerUpCapture?: React.PointerEventHandler | undefined; onPointerCancel?: React.PointerEventHandler | undefined; onPointerCancelCapture?: React.PointerEventHandler | undefined; onPointerEnter?: React.PointerEventHandler | undefined; onPointerEnterCapture?: React.PointerEventHandler | undefined; onPointerLeave?: React.PointerEventHandler | undefined; onPointerLeaveCapture?: React.PointerEventHandler | undefined; onPointerOver?: React.PointerEventHandler | undefined; onPointerOverCapture?: React.PointerEventHandler | undefined; onPointerOut?: React.PointerEventHandler | undefined; onPointerOutCapture?: React.PointerEventHandler | undefined; onGotPointerCapture?: React.PointerEventHandler | undefined; onGotPointerCaptureCapture?: React.PointerEventHandler | undefined; onLostPointerCapture?: React.PointerEventHandler | undefined; onLostPointerCaptureCapture?: React.PointerEventHandler | undefined; onScroll?: React.UIEventHandler | undefined; onScrollCapture?: React.UIEventHandler | undefined; onWheel?: React.WheelEventHandler | undefined; onWheelCapture?: React.WheelEventHandler | undefined; onAnimationStart?: React.AnimationEventHandler | undefined; onAnimationStartCapture?: React.AnimationEventHandler | undefined; onAnimationEnd?: React.AnimationEventHandler | undefined; onAnimationEndCapture?: React.AnimationEventHandler | undefined; onAnimationIteration?: React.AnimationEventHandler | undefined; onAnimationIterationCapture?: React.AnimationEventHandler | undefined; onTransitionEnd?: React.TransitionEventHandler | undefined; onTransitionEndCapture?: React.TransitionEventHandler | undefined; paddingSize?: \"m\" | \"none\" | \"s\" | \"xs\" | \"l\" | \"xl\" | undefined; href?: string | undefined; rel?: string | undefined; target?: string | undefined; icon?: React.ReactElement<", + ">; defaultChecked?: boolean | undefined; suppressContentEditableWarning?: boolean | undefined; suppressHydrationWarning?: boolean | undefined; accessKey?: string | undefined; contentEditable?: Booleanish | \"inherit\" | undefined; contextMenu?: string | undefined; dir?: string | undefined; draggable?: Booleanish | undefined; hidden?: boolean | undefined; lang?: string | undefined; placeholder?: string | undefined; spellCheck?: Booleanish | undefined; tabIndex?: number | undefined; translate?: \"yes\" | \"no\" | undefined; radioGroup?: string | undefined; role?: React.AriaRole | undefined; about?: string | undefined; datatype?: string | undefined; inlist?: any; property?: string | undefined; resource?: string | undefined; typeof?: string | undefined; vocab?: string | undefined; autoCapitalize?: string | undefined; autoCorrect?: string | undefined; autoSave?: string | undefined; itemProp?: string | undefined; itemScope?: boolean | undefined; itemType?: string | undefined; itemID?: string | undefined; itemRef?: string | undefined; results?: number | undefined; security?: string | undefined; unselectable?: \"on\" | \"off\" | undefined; inputMode?: \"search\" | \"none\" | \"text\" | \"tel\" | \"url\" | \"email\" | \"numeric\" | \"decimal\" | undefined; 'aria-activedescendant'?: string | undefined; 'aria-atomic'?: Booleanish | undefined; 'aria-autocomplete'?: \"none\" | \"list\" | \"inline\" | \"both\" | undefined; 'aria-busy'?: Booleanish | undefined; 'aria-checked'?: boolean | \"true\" | \"false\" | \"mixed\" | undefined; 'aria-colcount'?: number | undefined; 'aria-colindex'?: number | undefined; 'aria-colspan'?: number | undefined; 'aria-controls'?: string | undefined; 'aria-current'?: boolean | \"page\" | \"date\" | \"time\" | \"true\" | \"false\" | \"step\" | \"location\" | undefined; 'aria-describedby'?: string | undefined; 'aria-details'?: string | undefined; 'aria-disabled'?: Booleanish | undefined; 'aria-dropeffect'?: \"execute\" | \"link\" | \"none\" | \"copy\" | \"move\" | \"popup\" | undefined; 'aria-errormessage'?: string | undefined; 'aria-expanded'?: Booleanish | undefined; 'aria-flowto'?: string | undefined; 'aria-grabbed'?: Booleanish | undefined; 'aria-haspopup'?: boolean | \"dialog\" | \"menu\" | \"true\" | \"false\" | \"grid\" | \"listbox\" | \"tree\" | undefined; 'aria-hidden'?: Booleanish | undefined; 'aria-invalid'?: boolean | \"true\" | \"false\" | \"grammar\" | \"spelling\" | undefined; 'aria-keyshortcuts'?: string | undefined; 'aria-labelledby'?: string | undefined; 'aria-level'?: number | undefined; 'aria-live'?: \"off\" | \"assertive\" | \"polite\" | undefined; 'aria-modal'?: Booleanish | undefined; 'aria-multiline'?: Booleanish | undefined; 'aria-multiselectable'?: Booleanish | undefined; 'aria-orientation'?: \"horizontal\" | \"vertical\" | undefined; 'aria-owns'?: string | undefined; 'aria-placeholder'?: string | undefined; 'aria-posinset'?: number | undefined; 'aria-pressed'?: boolean | \"true\" | \"false\" | \"mixed\" | undefined; 'aria-readonly'?: Booleanish | undefined; 'aria-relevant'?: \"text\" | \"all\" | \"additions\" | \"additions removals\" | \"additions text\" | \"removals\" | \"removals additions\" | \"removals text\" | \"text additions\" | \"text removals\" | undefined; 'aria-required'?: Booleanish | undefined; 'aria-roledescription'?: string | undefined; 'aria-rowcount'?: number | undefined; 'aria-rowindex'?: number | undefined; 'aria-rowspan'?: number | undefined; 'aria-selected'?: Booleanish | undefined; 'aria-setsize'?: number | undefined; 'aria-sort'?: \"none\" | \"ascending\" | \"descending\" | \"other\" | undefined; 'aria-valuemax'?: number | undefined; 'aria-valuemin'?: number | undefined; 'aria-valuenow'?: number | undefined; 'aria-valuetext'?: string | undefined; dangerouslySetInnerHTML?: { __html: string; } | undefined; onCopy?: React.ClipboardEventHandler | undefined; onCopyCapture?: React.ClipboardEventHandler | undefined; onCut?: React.ClipboardEventHandler | undefined; onCutCapture?: React.ClipboardEventHandler | undefined; onPaste?: React.ClipboardEventHandler | undefined; onPasteCapture?: React.ClipboardEventHandler | undefined; onCompositionEnd?: React.CompositionEventHandler | undefined; onCompositionEndCapture?: React.CompositionEventHandler | undefined; onCompositionStart?: React.CompositionEventHandler | undefined; onCompositionStartCapture?: React.CompositionEventHandler | undefined; onCompositionUpdate?: React.CompositionEventHandler | undefined; onCompositionUpdateCapture?: React.CompositionEventHandler | undefined; onFocus?: React.FocusEventHandler | undefined; onFocusCapture?: React.FocusEventHandler | undefined; onBlur?: React.FocusEventHandler | undefined; onBlurCapture?: React.FocusEventHandler | undefined; onChange?: React.FormEventHandler | undefined; onChangeCapture?: React.FormEventHandler | undefined; onBeforeInput?: React.FormEventHandler | undefined; onBeforeInputCapture?: React.FormEventHandler | undefined; onInput?: React.FormEventHandler | undefined; onInputCapture?: React.FormEventHandler | undefined; onReset?: React.FormEventHandler | undefined; onResetCapture?: React.FormEventHandler | undefined; onSubmit?: React.FormEventHandler | undefined; onSubmitCapture?: React.FormEventHandler | undefined; onInvalid?: React.FormEventHandler | undefined; onInvalidCapture?: React.FormEventHandler | undefined; onLoad?: React.ReactEventHandler | undefined; onLoadCapture?: React.ReactEventHandler | undefined; onError?: React.ReactEventHandler | undefined; onErrorCapture?: React.ReactEventHandler | undefined; onKeyDown?: React.KeyboardEventHandler | undefined; onKeyDownCapture?: React.KeyboardEventHandler | undefined; onKeyPress?: React.KeyboardEventHandler | undefined; onKeyPressCapture?: React.KeyboardEventHandler | undefined; onKeyUp?: React.KeyboardEventHandler | undefined; onKeyUpCapture?: React.KeyboardEventHandler | undefined; onAbort?: React.ReactEventHandler | undefined; onAbortCapture?: React.ReactEventHandler | undefined; onCanPlay?: React.ReactEventHandler | undefined; onCanPlayCapture?: React.ReactEventHandler | undefined; onCanPlayThrough?: React.ReactEventHandler | undefined; onCanPlayThroughCapture?: React.ReactEventHandler | undefined; onDurationChange?: React.ReactEventHandler | undefined; onDurationChangeCapture?: React.ReactEventHandler | undefined; onEmptied?: React.ReactEventHandler | undefined; onEmptiedCapture?: React.ReactEventHandler | undefined; onEncrypted?: React.ReactEventHandler | undefined; onEncryptedCapture?: React.ReactEventHandler | undefined; onEnded?: React.ReactEventHandler | undefined; onEndedCapture?: React.ReactEventHandler | undefined; onLoadedData?: React.ReactEventHandler | undefined; onLoadedDataCapture?: React.ReactEventHandler | undefined; onLoadedMetadata?: React.ReactEventHandler | undefined; onLoadedMetadataCapture?: React.ReactEventHandler | undefined; onLoadStart?: React.ReactEventHandler | undefined; onLoadStartCapture?: React.ReactEventHandler | undefined; onPause?: React.ReactEventHandler | undefined; onPauseCapture?: React.ReactEventHandler | undefined; onPlay?: React.ReactEventHandler | undefined; onPlayCapture?: React.ReactEventHandler | undefined; onPlaying?: React.ReactEventHandler | undefined; onPlayingCapture?: React.ReactEventHandler | undefined; onProgress?: React.ReactEventHandler | undefined; onProgressCapture?: React.ReactEventHandler | undefined; onRateChange?: React.ReactEventHandler | undefined; onRateChangeCapture?: React.ReactEventHandler | undefined; onSeeked?: React.ReactEventHandler | undefined; onSeekedCapture?: React.ReactEventHandler | undefined; onSeeking?: React.ReactEventHandler | undefined; onSeekingCapture?: React.ReactEventHandler | undefined; onStalled?: React.ReactEventHandler | undefined; onStalledCapture?: React.ReactEventHandler | undefined; onSuspend?: React.ReactEventHandler | undefined; onSuspendCapture?: React.ReactEventHandler | undefined; onTimeUpdate?: React.ReactEventHandler | undefined; onTimeUpdateCapture?: React.ReactEventHandler | undefined; onVolumeChange?: React.ReactEventHandler | undefined; onVolumeChangeCapture?: React.ReactEventHandler | undefined; onWaiting?: React.ReactEventHandler | undefined; onWaitingCapture?: React.ReactEventHandler | undefined; onAuxClick?: React.MouseEventHandler | undefined; onAuxClickCapture?: React.MouseEventHandler | undefined; onClick?: React.MouseEventHandler | undefined; onClickCapture?: React.MouseEventHandler | undefined; onContextMenu?: React.MouseEventHandler | undefined; onContextMenuCapture?: React.MouseEventHandler | undefined; onDoubleClick?: React.MouseEventHandler | undefined; onDoubleClickCapture?: React.MouseEventHandler | undefined; onDrag?: React.DragEventHandler | undefined; onDragCapture?: React.DragEventHandler | undefined; onDragEnd?: React.DragEventHandler | undefined; onDragEndCapture?: React.DragEventHandler | undefined; onDragEnter?: React.DragEventHandler | undefined; onDragEnterCapture?: React.DragEventHandler | undefined; onDragExit?: React.DragEventHandler | undefined; onDragExitCapture?: React.DragEventHandler | undefined; onDragLeave?: React.DragEventHandler | undefined; onDragLeaveCapture?: React.DragEventHandler | undefined; onDragOver?: React.DragEventHandler | undefined; onDragOverCapture?: React.DragEventHandler | undefined; onDragStart?: React.DragEventHandler | undefined; onDragStartCapture?: React.DragEventHandler | undefined; onDrop?: React.DragEventHandler | undefined; onDropCapture?: React.DragEventHandler | undefined; onMouseDown?: React.MouseEventHandler | undefined; onMouseDownCapture?: React.MouseEventHandler | undefined; onMouseEnter?: React.MouseEventHandler | undefined; onMouseLeave?: React.MouseEventHandler | undefined; onMouseMove?: React.MouseEventHandler | undefined; onMouseMoveCapture?: React.MouseEventHandler | undefined; onMouseOut?: React.MouseEventHandler | undefined; onMouseOutCapture?: React.MouseEventHandler | undefined; onMouseOver?: React.MouseEventHandler | undefined; onMouseOverCapture?: React.MouseEventHandler | undefined; onMouseUp?: React.MouseEventHandler | undefined; onMouseUpCapture?: React.MouseEventHandler | undefined; onSelect?: React.ReactEventHandler | undefined; onSelectCapture?: React.ReactEventHandler | undefined; onTouchCancel?: React.TouchEventHandler | undefined; onTouchCancelCapture?: React.TouchEventHandler | undefined; onTouchEnd?: React.TouchEventHandler | undefined; onTouchEndCapture?: React.TouchEventHandler | undefined; onTouchMove?: React.TouchEventHandler | undefined; onTouchMoveCapture?: React.TouchEventHandler | undefined; onTouchStart?: React.TouchEventHandler | undefined; onTouchStartCapture?: React.TouchEventHandler | undefined; onPointerDown?: React.PointerEventHandler | undefined; onPointerDownCapture?: React.PointerEventHandler | undefined; onPointerMove?: React.PointerEventHandler | undefined; onPointerMoveCapture?: React.PointerEventHandler | undefined; onPointerUp?: React.PointerEventHandler | undefined; onPointerUpCapture?: React.PointerEventHandler | undefined; onPointerCancel?: React.PointerEventHandler | undefined; onPointerCancelCapture?: React.PointerEventHandler | undefined; onPointerEnter?: React.PointerEventHandler | undefined; onPointerEnterCapture?: React.PointerEventHandler | undefined; onPointerLeave?: React.PointerEventHandler | undefined; onPointerLeaveCapture?: React.PointerEventHandler | undefined; onPointerOver?: React.PointerEventHandler | undefined; onPointerOverCapture?: React.PointerEventHandler | undefined; onPointerOut?: React.PointerEventHandler | undefined; onPointerOutCapture?: React.PointerEventHandler | undefined; onGotPointerCapture?: React.PointerEventHandler | undefined; onGotPointerCaptureCapture?: React.PointerEventHandler | undefined; onLostPointerCapture?: React.PointerEventHandler | undefined; onLostPointerCaptureCapture?: React.PointerEventHandler | undefined; onScroll?: React.UIEventHandler | undefined; onScrollCapture?: React.UIEventHandler | undefined; onWheel?: React.WheelEventHandler | undefined; onWheelCapture?: React.WheelEventHandler | undefined; onAnimationStart?: React.AnimationEventHandler | undefined; onAnimationStartCapture?: React.AnimationEventHandler | undefined; onAnimationEnd?: React.AnimationEventHandler | undefined; onAnimationEndCapture?: React.AnimationEventHandler | undefined; onAnimationIteration?: React.AnimationEventHandler | undefined; onAnimationIterationCapture?: React.AnimationEventHandler | undefined; onTransitionEnd?: React.TransitionEventHandler | undefined; onTransitionEndCapture?: React.TransitionEventHandler | undefined; paddingSize?: \"m\" | \"none\" | \"s\" | \"xs\" | \"l\" | \"xl\" | undefined; href?: string | undefined; rel?: string | undefined; target?: string | undefined; icon?: React.ReactElement<", "EuiIconProps", ", string | React.JSXElementConstructor> | null | undefined; hasBorder?: boolean | undefined; textAlign?: \"right\" | \"left\" | \"center\" | undefined; titleElement?: \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"p\" | \"span\" | undefined; titleSize?: \"s\" | \"xs\" | undefined; betaBadgeProps?: (", "CommonProps", diff --git a/api_docs/kbn_shared_ux_card_no_data.mdx b/api_docs/kbn_shared_ux_card_no_data.mdx index 76d5b21aa939c..168ffd1305214 100644 --- a/api_docs/kbn_shared_ux_card_no_data.mdx +++ b/api_docs/kbn_shared_ux_card_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data title: "@kbn/shared-ux-card-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data plugin -date: 2023-02-23 +date: 2023-02-28 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 a2aac4260a3c0..df21948c046b9 100644 --- a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data-mocks title: "@kbn/shared-ux-card-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data-mocks plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data-mocks'] --- import kbnSharedUxCardNoDataMocksObj from './kbn_shared_ux_card_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_context.devdocs.json b/api_docs/kbn_shared_ux_file_context.devdocs.json index 02025b778807c..a48e1addf9ede 100644 --- a/api_docs/kbn_shared_ux_file_context.devdocs.json +++ b/api_docs/kbn_shared_ux_file_context.devdocs.json @@ -99,7 +99,13 @@ "\nA files client that will be used process uploads." ], "signature": [ - "BaseFilesClient", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.BaseFilesClient", + "text": "BaseFilesClient" + }, "" ], "path": "packages/shared-ux/file/context/src/index.tsx", diff --git a/api_docs/kbn_shared_ux_file_context.mdx b/api_docs/kbn_shared_ux_file_context.mdx index 32188074a822a..17a197cb50150 100644 --- a/api_docs/kbn_shared_ux_file_context.mdx +++ b/api_docs/kbn_shared_ux_file_context.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-context title: "@kbn/shared-ux-file-context" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-context plugin -date: 2023-02-23 +date: 2023-02-28 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.devdocs.json b/api_docs/kbn_shared_ux_file_image.devdocs.json index 5d3350f6cf444..4dfade1406167 100644 --- a/api_docs/kbn_shared_ux_file_image.devdocs.json +++ b/api_docs/kbn_shared_ux_file_image.devdocs.json @@ -83,7 +83,13 @@ "description": [], "signature": [ "{ meta?: ", - "FileImageMetadata", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileImageMetadata", + "text": "FileImageMetadata" + }, " | undefined; } & ", "EuiImageProps" ], diff --git a/api_docs/kbn_shared_ux_file_image.mdx b/api_docs/kbn_shared_ux_file_image.mdx index 6a664d5f77c23..4ca4a262aac6c 100644 --- a/api_docs/kbn_shared_ux_file_image.mdx +++ b/api_docs/kbn_shared_ux_file_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image title: "@kbn/shared-ux-file-image" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image plugin -date: 2023-02-23 +date: 2023-02-28 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 3aea39006e132..3bd8feca9cc06 100644 --- a/api_docs/kbn_shared_ux_file_image_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_image_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image-mocks title: "@kbn/shared-ux-file-image-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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.devdocs.json b/api_docs/kbn_shared_ux_file_mocks.devdocs.json index ecdb910452dba..1a16eeed01e56 100644 --- a/api_docs/kbn_shared_ux_file_mocks.devdocs.json +++ b/api_docs/kbn_shared_ux_file_mocks.devdocs.json @@ -36,7 +36,13 @@ "text": "DeeplyMockedKeys" }, "<", - "BaseFilesClient", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.BaseFilesClient", + "text": "BaseFilesClient" + }, ">" ], "path": "packages/shared-ux/file/mocks/index.ts", diff --git a/api_docs/kbn_shared_ux_file_mocks.mdx b/api_docs/kbn_shared_ux_file_mocks.mdx index 63295d97ca4da..14e59f85eee8b 100644 --- a/api_docs/kbn_shared_ux_file_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-mocks title: "@kbn/shared-ux-file-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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.devdocs.json b/api_docs/kbn_shared_ux_file_picker.devdocs.json index 0838853073257..1ecf0fc909e4e 100644 --- a/api_docs/kbn_shared_ux_file_picker.devdocs.json +++ b/api_docs/kbn_shared_ux_file_picker.devdocs.json @@ -117,7 +117,13 @@ ], "signature": [ "((file: ", - "FileJSON", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileJSON", + "text": "FileJSON" + }, ") => boolean) | undefined" ], "path": "packages/shared-ux/file/file_picker/impl/src/file_picker.tsx", @@ -132,7 +138,13 @@ "label": "file", "description": [], "signature": [ - "FileJSON", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileJSON", + "text": "FileJSON" + }, "" ], "path": "packages/shared-ux/file/file_picker/impl/src/file_picker.tsx", @@ -172,7 +184,13 @@ ], "signature": [ "(files: ", - "FileJSON", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileJSON", + "text": "FileJSON" + }, "[]) => void" ], "path": "packages/shared-ux/file/file_picker/impl/src/file_picker.tsx", @@ -187,7 +205,13 @@ "label": "files", "description": [], "signature": [ - "FileJSON", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileJSON", + "text": "FileJSON" + }, "[]" ], "path": "packages/shared-ux/file/file_picker/impl/src/file_picker.tsx", diff --git a/api_docs/kbn_shared_ux_file_picker.mdx b/api_docs/kbn_shared_ux_file_picker.mdx index 43b4a53dba53a..f4abb5309c5e6 100644 --- a/api_docs/kbn_shared_ux_file_picker.mdx +++ b/api_docs/kbn_shared_ux_file_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-picker title: "@kbn/shared-ux-file-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-picker plugin -date: 2023-02-23 +date: 2023-02-28 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.devdocs.json b/api_docs/kbn_shared_ux_file_types.devdocs.json new file mode 100644 index 0000000000000..de6c162159d55 --- /dev/null +++ b/api_docs/kbn_shared_ux_file_types.devdocs.json @@ -0,0 +1,1581 @@ +{ + "id": "@kbn/shared-ux-file-types", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [ + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.Abortable", + "type": "Interface", + "tags": [], + "label": "Abortable", + "description": [], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.Abortable.abortSignal", + "type": "Object", + "tags": [], + "label": "abortSignal", + "description": [], + "signature": [ + "AbortSignal | undefined" + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFilesClient", + "type": "Interface", + "tags": [], + "label": "BaseFilesClient", + "description": [], + "signature": [ + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.BaseFilesClient", + "text": "BaseFilesClient" + }, + "" + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFilesClient.find", + "type": "Function", + "tags": [], + "label": "find", + "description": [ + "\nFind a set of files given some filters.\n" + ], + "signature": [ + "(args: { kind?: string | string[] | undefined; status?: string | string[] | undefined; extension?: string | string[] | undefined; name?: string | string[] | undefined; meta?: M | undefined; } & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Pagination", + "text": "Pagination" + }, + " & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + }, + ") => Promise<{ files: ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileJSON", + "text": "FileJSON" + }, + "[]; total: number; }>" + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFilesClient.find.$1", + "type": "CompoundType", + "tags": [], + "label": "args", + "description": [ + "- File filters" + ], + "signature": [ + "{ kind?: string | string[] | undefined; status?: string | string[] | undefined; extension?: string | string[] | undefined; name?: string | string[] | undefined; meta?: M | undefined; } & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Pagination", + "text": "Pagination" + }, + " & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + } + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFilesClient.bulkDelete", + "type": "Function", + "tags": [], + "label": "bulkDelete", + "description": [ + "\nBulk a delete a set of files given their IDs.\n" + ], + "signature": [ + "(args: { ids: string[]; } & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + }, + ") => Promise<{ succeeded: string[]; failed?: [id: string, reason: string][] | undefined; }>" + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFilesClient.bulkDelete.$1", + "type": "CompoundType", + "tags": [], + "label": "args", + "description": [ + "- Bulk delete args" + ], + "signature": [ + "{ ids: string[]; } & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + } + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFilesClient.create", + "type": "Function", + "tags": [], + "label": "create", + "description": [ + "\nCreate a new file object with the provided metadata.\n" + ], + "signature": [ + "(args: { name: string; meta?: M | undefined; alt?: string | undefined; mimeType?: string | undefined; kind: string; } & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + }, + ") => Promise<{ file: ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileJSON", + "text": "FileJSON" + }, + "; }>" + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFilesClient.create.$1", + "type": "CompoundType", + "tags": [], + "label": "args", + "description": [ + "- create file args" + ], + "signature": [ + "{ name: string; meta?: M | undefined; alt?: string | undefined; mimeType?: string | undefined; kind: string; } & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + } + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFilesClient.delete", + "type": "Function", + "tags": [], + "label": "delete", + "description": [ + "\nDelete a file object and all associated share and content objects.\n" + ], + "signature": [ + "(args: { id: string; kind: string; } & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + }, + ") => Promise<{ ok: true; }>" + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFilesClient.delete.$1", + "type": "CompoundType", + "tags": [], + "label": "args", + "description": [ + "- delete file args" + ], + "signature": [ + "{ id: string; kind: string; } & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + } + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFilesClient.getById", + "type": "Function", + "tags": [], + "label": "getById", + "description": [ + "\nGet a file object by ID.\n" + ], + "signature": [ + "(args: { id: string; kind: string; } & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + }, + ") => Promise<{ file: ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileJSON", + "text": "FileJSON" + }, + "; }>" + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFilesClient.getById.$1", + "type": "CompoundType", + "tags": [], + "label": "args", + "description": [ + "- get file by ID args" + ], + "signature": [ + "{ id: string; kind: string; } & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + } + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFilesClient.list", + "type": "Function", + "tags": [], + "label": "list", + "description": [ + "\nList all file objects, of a given {@link FileKindBrowser}.\n" + ], + "signature": [ + "(args: { kind: string; status?: string | string[] | undefined; extension?: string | string[] | undefined; name?: string | string[] | undefined; meta?: M | undefined; } & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Pagination", + "text": "Pagination" + }, + " & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + }, + ") => Promise<{ files: ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileJSON", + "text": "FileJSON" + }, + "[]; total: number; }>" + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFilesClient.list.$1", + "type": "CompoundType", + "tags": [], + "label": "args", + "description": [ + "- list files args" + ], + "signature": [ + "{ kind: string; status?: string | string[] | undefined; extension?: string | string[] | undefined; name?: string | string[] | undefined; meta?: M | undefined; } & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Pagination", + "text": "Pagination" + }, + " & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + } + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFilesClient.update", + "type": "Function", + "tags": [], + "label": "update", + "description": [ + "\nUpdate a set of of metadata values of the file object.\n" + ], + "signature": [ + "(args: { id: string; kind: string; name?: string | undefined; meta?: M | undefined; alt?: string | undefined; } & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + }, + ") => Promise<{ file: ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileJSON", + "text": "FileJSON" + }, + "; }>" + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFilesClient.update.$1", + "type": "CompoundType", + "tags": [], + "label": "args", + "description": [ + "- update file args" + ], + "signature": [ + "{ id: string; kind: string; name?: string | undefined; meta?: M | undefined; alt?: string | undefined; } & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + } + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFilesClient.upload", + "type": "Function", + "tags": [], + "label": "upload", + "description": [ + "\nStream the contents of the file to Kibana server for storage.\n" + ], + "signature": [ + "(args: { id: string; body: unknown; kind: string; abortSignal?: AbortSignal | undefined; contentType?: string | undefined; selfDestructOnAbort?: boolean | undefined; } & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + }, + ") => Promise<{ ok: true; size: number; }>" + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFilesClient.upload.$1", + "type": "CompoundType", + "tags": [], + "label": "args", + "description": [ + "- upload file args" + ], + "signature": [ + "{ id: string; body: unknown; kind: string; abortSignal?: AbortSignal | undefined; contentType?: string | undefined; selfDestructOnAbort?: boolean | undefined; } & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + } + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFilesClient.download", + "type": "Function", + "tags": [], + "label": "download", + "description": [ + "\nStream a download of the file object's content.\n" + ], + "signature": [ + "(args: { fileName?: string | undefined; id: string; kind: string; } & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + }, + ") => Promise" + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFilesClient.download.$1", + "type": "CompoundType", + "tags": [], + "label": "args", + "description": [ + "- download file args" + ], + "signature": [ + "{ fileName?: string | undefined; id: string; kind: string; } & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + } + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFilesClient.getDownloadHref", + "type": "Function", + "tags": [], + "label": "getDownloadHref", + "description": [ + "\nGet a string for downloading a file that can be passed to a button element's\nhref for download.\n" + ], + "signature": [ + "(args: Pick<", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileJSON", + "text": "FileJSON" + }, + ", \"id\" | \"fileKind\">) => string" + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFilesClient.getDownloadHref.$1", + "type": "Object", + "tags": [], + "label": "args", + "description": [ + "- get download URL args" + ], + "signature": [ + "Pick<", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileJSON", + "text": "FileJSON" + }, + ", \"id\" | \"fileKind\">" + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFilesClient.share", + "type": "Function", + "tags": [ + "note" + ], + "label": "share", + "description": [ + "\nShare a file by creating a new file share instance.\n" + ], + "signature": [ + "(args: { name?: string | undefined; validUntil?: number | undefined; fileId: string; kind: string; } & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + }, + ") => Promise<", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileShareJSONWithToken", + "text": "FileShareJSONWithToken" + }, + ">" + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFilesClient.share.$1", + "type": "CompoundType", + "tags": [], + "label": "args", + "description": [ + "- File share arguments" + ], + "signature": [ + "{ name?: string | undefined; validUntil?: number | undefined; fileId: string; kind: string; } & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + } + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFilesClient.unshare", + "type": "Function", + "tags": [], + "label": "unshare", + "description": [ + "\nDelete a file share instance.\n" + ], + "signature": [ + "(args: { id: string; kind: string; } & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + }, + ") => Promise<{ ok: true; }>" + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFilesClient.unshare.$1", + "type": "CompoundType", + "tags": [], + "label": "args", + "description": [ + "- File unshare arguments" + ], + "signature": [ + "{ id: string; kind: string; } & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + } + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFilesClient.getShare", + "type": "Function", + "tags": [], + "label": "getShare", + "description": [ + "\nGet a file share instance.\n" + ], + "signature": [ + "(args: { id: string; kind: string; } & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + }, + ") => Promise<{ share: ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileShareJSON", + "text": "FileShareJSON" + }, + "; }>" + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFilesClient.getShare.$1", + "type": "CompoundType", + "tags": [], + "label": "args", + "description": [ + "- Get file share arguments" + ], + "signature": [ + "{ id: string; kind: string; } & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + } + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFilesClient.listShares", + "type": "Function", + "tags": [], + "label": "listShares", + "description": [ + "\nList all file shares. Optionally scoping to a specific\nfile.\n" + ], + "signature": [ + "(args: { forFileId?: string | undefined; kind: string; } & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Pagination", + "text": "Pagination" + }, + " & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + }, + ") => Promise<{ shares: ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileShareJSON", + "text": "FileShareJSON" + }, + "[]; }>" + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFilesClient.listShares.$1", + "type": "CompoundType", + "tags": [], + "label": "args", + "description": [ + "- Get file share arguments" + ], + "signature": [ + "{ forFileId?: string | undefined; kind: string; } & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Pagination", + "text": "Pagination" + }, + " & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.Abortable", + "text": "Abortable" + } + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFilesClient.getFileKind", + "type": "Function", + "tags": [], + "label": "getFileKind", + "description": [ + "\nGet a file kind" + ], + "signature": [ + "(id: string) => ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileKindBase", + "text": "FileKindBase" + } + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFilesClient.getFileKind.$1", + "type": "string", + "tags": [], + "label": "id", + "description": [ + "The id of the file kind" + ], + "signature": [ + "string" + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileImageMetadata", + "type": "Interface", + "tags": [], + "label": "FileImageMetadata", + "description": [ + "\nSet of metadata captured for every image uploaded via the file services'\npublic components." + ], + "path": "packages/shared-ux/file/types/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileImageMetadata.blurhash", + "type": "string", + "tags": [], + "label": "blurhash", + "description": [ + "\nThe blurhash that can be displayed while the image is loading" + ], + "signature": [ + "string | undefined" + ], + "path": "packages/shared-ux/file/types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileImageMetadata.width", + "type": "number", + "tags": [], + "label": "width", + "description": [ + "\nWidth, in px, of the original image" + ], + "path": "packages/shared-ux/file/types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileImageMetadata.height", + "type": "number", + "tags": [], + "label": "height", + "description": [ + "\nHeight, in px, of the original image" + ], + "path": "packages/shared-ux/file/types/index.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileJSON", + "type": "Interface", + "tags": [], + "label": "FileJSON", + "description": [ + "\nAttributes of a file that represent a serialised version of the file." + ], + "signature": [ + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileJSON", + "text": "FileJSON" + }, + "" + ], + "path": "packages/shared-ux/file/types/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileJSON.id", + "type": "string", + "tags": [], + "label": "id", + "description": [ + "\nUnique file ID." + ], + "path": "packages/shared-ux/file/types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileJSON.created", + "type": "string", + "tags": [], + "label": "created", + "description": [ + "\nISO string of when this file was created" + ], + "path": "packages/shared-ux/file/types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileJSON.updated", + "type": "string", + "tags": [], + "label": "updated", + "description": [ + "\nISO string of when the file was updated" + ], + "path": "packages/shared-ux/file/types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileJSON.name", + "type": "string", + "tags": [ + "note" + ], + "label": "name", + "description": [ + "\nFile name.\n" + ], + "path": "packages/shared-ux/file/types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileJSON.mimeType", + "type": "string", + "tags": [], + "label": "mimeType", + "description": [ + "\nMIME type of the file's contents." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/shared-ux/file/types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileJSON.size", + "type": "number", + "tags": [], + "label": "size", + "description": [ + "\nThe size, in bytes, of the file content." + ], + "signature": [ + "number | undefined" + ], + "path": "packages/shared-ux/file/types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileJSON.extension", + "type": "string", + "tags": [ + "note" + ], + "label": "extension", + "description": [ + "\nThe file extension (dot suffix).\n" + ], + "signature": [ + "string | undefined" + ], + "path": "packages/shared-ux/file/types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileJSON.meta", + "type": "Uncategorized", + "tags": [], + "label": "meta", + "description": [ + "\nA consumer defined set of attributes.\n\nConsumers of the file service can add their own tags and identifiers to\na file using the \"meta\" object." + ], + "signature": [ + "Meta | undefined" + ], + "path": "packages/shared-ux/file/types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileJSON.alt", + "type": "string", + "tags": [], + "label": "alt", + "description": [ + "\nUse this text to describe the file contents for display and accessibility." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/shared-ux/file/types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileJSON.fileKind", + "type": "string", + "tags": [ + "note" + ], + "label": "fileKind", + "description": [ + "\nA unique kind that governs various aspects of the file. A consumer of the\nfiles service must register a file kind and link their files to a specific\nkind.\n" + ], + "path": "packages/shared-ux/file/types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileJSON.status", + "type": "CompoundType", + "tags": [], + "label": "status", + "description": [ + "\nThe current status of the file.\n\nSee {@link FileStatus} for more details." + ], + "signature": [ + "\"AWAITING_UPLOAD\" | \"UPLOADING\" | \"READY\" | \"UPLOAD_ERROR\" | \"DELETED\"" + ], + "path": "packages/shared-ux/file/types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileJSON.user", + "type": "Object", + "tags": [], + "label": "user", + "description": [ + "\nUser data associated with this file" + ], + "signature": [ + "{ name?: string | undefined; id?: string | undefined; } | undefined" + ], + "path": "packages/shared-ux/file/types/index.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileKindBase", + "type": "Interface", + "tags": [], + "label": "FileKindBase", + "description": [], + "path": "packages/shared-ux/file/types/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileKindBase.id", + "type": "string", + "tags": [], + "label": "id", + "description": [ + "\nUnique file kind ID" + ], + "path": "packages/shared-ux/file/types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileKindBase.allowedMimeTypes", + "type": "Array", + "tags": [ + "default" + ], + "label": "allowedMimeTypes", + "description": [ + "\nThe MIME type of the file content.\n" + ], + "signature": [ + "string[] | undefined" + ], + "path": "packages/shared-ux/file/types/index.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileKindBrowser", + "type": "Interface", + "tags": [], + "label": "FileKindBrowser", + "description": [], + "signature": [ + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileKindBrowser", + "text": "FileKindBrowser" + }, + " extends ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileKindBase", + "text": "FileKindBase" + } + ], + "path": "packages/shared-ux/file/types/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileKindBrowser.maxSizeBytes", + "type": "number", + "tags": [ + "default" + ], + "label": "maxSizeBytes", + "description": [ + "\nMax file contents size, in bytes, enforced for this file kind in the upload\ncomponent.\n" + ], + "signature": [ + "number | undefined" + ], + "path": "packages/shared-ux/file/types/index.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileShareJSON", + "type": "Interface", + "tags": [], + "label": "FileShareJSON", + "description": [ + "\nAttributes of a file that represent a serialised version of the file." + ], + "path": "packages/shared-ux/file/types/sharing.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileShareJSON.id", + "type": "string", + "tags": [], + "label": "id", + "description": [ + "\nUnique ID share instance" + ], + "path": "packages/shared-ux/file/types/sharing.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileShareJSON.created", + "type": "string", + "tags": [], + "label": "created", + "description": [ + "\nISO timestamp the share was created" + ], + "path": "packages/shared-ux/file/types/sharing.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileShareJSON.validUntil", + "type": "number", + "tags": [], + "label": "validUntil", + "description": [ + "\nUnix timestamp (in milliseconds) of when this share expires" + ], + "path": "packages/shared-ux/file/types/sharing.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileShareJSON.name", + "type": "string", + "tags": [], + "label": "name", + "description": [ + "\nA user-friendly name for the file share" + ], + "signature": [ + "string | undefined" + ], + "path": "packages/shared-ux/file/types/sharing.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileShareJSON.fileId", + "type": "string", + "tags": [], + "label": "fileId", + "description": [ + "\nThe ID of the file this share is linked to" + ], + "path": "packages/shared-ux/file/types/sharing.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.Pagination", + "type": "Interface", + "tags": [], + "label": "Pagination", + "description": [], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.Pagination.page", + "type": "number", + "tags": [], + "label": "page", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.Pagination.perPage", + "type": "number", + "tags": [], + "label": "perPage", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "packages/shared-ux/file/types/base_file_client.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.BaseFileMetadata", + "type": "Type", + "tags": [], + "label": "BaseFileMetadata", + "description": [ + "\nFile metadata fields are defined per the ECS specification:\n\nhttps://www.elastic.co/guide/en/ecs/current/ecs-file.html\n\nCustom fields are named according to the custom field convention: \"CustomFieldName\"." + ], + "signature": [ + "{ name?: string | undefined; mime_type?: string | undefined; created?: string | undefined; size?: number | undefined; hash?: { [hashName: string]: string | undefined; md5?: string | undefined; sha1?: string | undefined; sha256?: string | undefined; sha384?: string | undefined; sha512?: string | undefined; ssdeep?: string | undefined; tlsh?: string | undefined; } | undefined; user?: { name?: string | undefined; id?: string | undefined; } | undefined; extension?: string | undefined; Alt?: string | undefined; Updated?: string | undefined; Status?: ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileStatus", + "text": "FileStatus" + }, + " | undefined; ChunkSize?: number | undefined; Compression?: ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileCompression", + "text": "FileCompression" + }, + " | undefined; }" + ], + "path": "packages/shared-ux/file/types/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileCompression", + "type": "Type", + "tags": [], + "label": "FileCompression", + "description": [ + "\nSupported file compression algorithms" + ], + "signature": [ + "\"none\" | \"br\" | \"gzip\" | \"deflate\"" + ], + "path": "packages/shared-ux/file/types/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileMetadata", + "type": "Type", + "tags": [], + "label": "FileMetadata", + "description": [ + "\nExtra metadata on a file object specific to Kibana implementation." + ], + "signature": [ + "Required> & ", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.BaseFileMetadata", + "text": "BaseFileMetadata" + }, + " & { FileKind: string; Meta?: Meta | undefined; }" + ], + "path": "packages/shared-ux/file/types/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileShare", + "type": "Type", + "tags": [], + "label": "FileShare", + "description": [ + "\nData stored with a file share object" + ], + "signature": [ + "{ created: string; token: string; name?: string | undefined; valid_until: number; }" + ], + "path": "packages/shared-ux/file/types/sharing.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileShareJSONWithToken", + "type": "Type", + "tags": [ + "note" + ], + "label": "FileShareJSONWithToken", + "description": [ + "\nA version of the file share with a token included.\n" + ], + "signature": [ + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileShareJSON", + "text": "FileShareJSON" + }, + " & { token: string; }" + ], + "path": "packages/shared-ux/file/types/sharing.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/shared-ux-file-types", + "id": "def-common.FileStatus", + "type": "Type", + "tags": [], + "label": "FileStatus", + "description": [], + "signature": [ + "\"AWAITING_UPLOAD\" | \"UPLOADING\" | \"READY\" | \"UPLOAD_ERROR\" | \"DELETED\"" + ], + "path": "packages/shared-ux/file/types/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_shared_ux_file_types.mdx b/api_docs/kbn_shared_ux_file_types.mdx new file mode 100644 index 0000000000000..8209bfcad95c2 --- /dev/null +++ b/api_docs/kbn_shared_ux_file_types.mdx @@ -0,0 +1,33 @@ +--- +#### +#### 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: kibKbnSharedUxFileTypesPluginApi +slug: /kibana-dev-docs/api/kbn-shared-ux-file-types +title: "@kbn/shared-ux-file-types" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/shared-ux-file-types plugin +date: 2023-02-28 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-types'] +--- +import kbnSharedUxFileTypesObj from './kbn_shared_ux_file_types.devdocs.json'; + + + +Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 70 | 0 | 9 | 0 | + +## Common + +### Interfaces + + +### Consts, variables and types + + diff --git a/api_docs/kbn_shared_ux_file_upload.devdocs.json b/api_docs/kbn_shared_ux_file_upload.devdocs.json index 143e11c571ef2..82ccfb8816f4f 100644 --- a/api_docs/kbn_shared_ux_file_upload.devdocs.json +++ b/api_docs/kbn_shared_ux_file_upload.devdocs.json @@ -119,7 +119,13 @@ "label": "fileJSON", "description": [], "signature": [ - "FileJSON", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileJSON", + "text": "FileJSON" + }, "" ], "path": "packages/shared-ux/file/file_upload/impl/src/upload_state.ts", diff --git a/api_docs/kbn_shared_ux_file_upload.mdx b/api_docs/kbn_shared_ux_file_upload.mdx index b8b59b33d3da5..4a7f1131800f7 100644 --- a/api_docs/kbn_shared_ux_file_upload.mdx +++ b/api_docs/kbn_shared_ux_file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-upload title: "@kbn/shared-ux-file-upload" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-upload plugin -date: 2023-02-23 +date: 2023-02-28 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.devdocs.json b/api_docs/kbn_shared_ux_file_util.devdocs.json index c471a74aeec8e..bf89b1d3e11a3 100644 --- a/api_docs/kbn_shared_ux_file_util.devdocs.json +++ b/api_docs/kbn_shared_ux_file_util.devdocs.json @@ -144,7 +144,13 @@ ], "signature": [ "(file: Blob | File) => Promise<", - "FileImageMetadata", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileImageMetadata", + "text": "FileImageMetadata" + }, " | undefined>" ], "path": "packages/shared-ux/file/util/src/image_metadata.ts", @@ -264,7 +270,13 @@ "description": [], "signature": [ "(file: Blob | File) => Promise<", - "FileImageMetadata", + { + "pluginId": "@kbn/shared-ux-file-types", + "scope": "common", + "docId": "kibKbnSharedUxFileTypesPluginApi", + "section": "def-common.FileImageMetadata", + "text": "FileImageMetadata" + }, " | undefined>" ], "path": "packages/shared-ux/file/util/src/image_metadata.ts", diff --git a/api_docs/kbn_shared_ux_file_util.mdx b/api_docs/kbn_shared_ux_file_util.mdx index 8c1e0e95ab386..b769fcf74f510 100644 --- a/api_docs/kbn_shared_ux_file_util.mdx +++ b/api_docs/kbn_shared_ux_file_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-util title: "@kbn/shared-ux-file-util" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-util plugin -date: 2023-02-23 +date: 2023-02-28 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 67f643a0a4ed0..3e784736600a3 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app title: "@kbn/shared-ux-link-redirect-app" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app plugin -date: 2023-02-23 +date: 2023-02-28 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 881f672bb6647..fc64a58d11b5b 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app-mocks title: "@kbn/shared-ux-link-redirect-app-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 5c0ad1268f9e4..940644b3fd59d 100644 --- a/api_docs/kbn_shared_ux_markdown.mdx +++ b/api_docs/kbn_shared_ux_markdown.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown title: "@kbn/shared-ux-markdown" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown plugin -date: 2023-02-23 +date: 2023-02-28 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.devdocs.json b/api_docs/kbn_shared_ux_markdown_mocks.devdocs.json index e6fca98d31d64..1dbb1d5e4e58e 100644 --- a/api_docs/kbn_shared_ux_markdown_mocks.devdocs.json +++ b/api_docs/kbn_shared_ux_markdown_mocks.devdocs.json @@ -439,7 +439,7 @@ "Interpolation", "<", "Theme", - ">; defaultChecked?: boolean | undefined; suppressContentEditableWarning?: boolean | undefined; suppressHydrationWarning?: boolean | undefined; accessKey?: string | undefined; contentEditable?: Booleanish | \"inherit\" | undefined; contextMenu?: string | undefined; dir?: string | undefined; draggable?: Booleanish | undefined; hidden?: boolean | undefined; lang?: string | undefined; placeholder?: string | undefined; spellCheck?: Booleanish | undefined; tabIndex?: number | undefined; translate?: \"yes\" | \"no\" | undefined; radioGroup?: string | undefined; role?: React.AriaRole | undefined; about?: string | undefined; datatype?: string | undefined; inlist?: any; property?: string | undefined; resource?: string | undefined; typeof?: string | undefined; vocab?: string | undefined; autoCapitalize?: string | undefined; autoCorrect?: string | undefined; autoSave?: string | undefined; color?: string | undefined; itemProp?: string | undefined; itemScope?: boolean | undefined; itemType?: string | undefined; itemID?: string | undefined; itemRef?: string | undefined; results?: number | undefined; security?: string | undefined; unselectable?: \"on\" | \"off\" | undefined; inputMode?: \"search\" | \"none\" | \"text\" | \"tel\" | \"url\" | \"email\" | \"numeric\" | \"decimal\" | undefined; 'aria-activedescendant'?: string | undefined; 'aria-atomic'?: Booleanish | undefined; 'aria-autocomplete'?: \"none\" | \"list\" | \"inline\" | \"both\" | undefined; 'aria-busy'?: Booleanish | undefined; 'aria-checked'?: boolean | \"true\" | \"false\" | \"mixed\" | undefined; 'aria-colcount'?: number | undefined; 'aria-colindex'?: number | undefined; 'aria-colspan'?: number | undefined; 'aria-controls'?: string | undefined; 'aria-current'?: boolean | \"page\" | \"date\" | \"time\" | \"true\" | \"false\" | \"step\" | \"location\" | undefined; 'aria-describedby'?: string | undefined; 'aria-details'?: string | undefined; 'aria-disabled'?: Booleanish | undefined; 'aria-dropeffect'?: \"execute\" | \"link\" | \"none\" | \"copy\" | \"move\" | \"popup\" | undefined; 'aria-errormessage'?: string | undefined; 'aria-expanded'?: Booleanish | undefined; 'aria-flowto'?: string | undefined; 'aria-grabbed'?: Booleanish | undefined; 'aria-haspopup'?: boolean | \"dialog\" | \"menu\" | \"true\" | \"false\" | \"grid\" | \"listbox\" | \"tree\" | undefined; 'aria-hidden'?: Booleanish | undefined; 'aria-invalid'?: boolean | \"true\" | \"false\" | \"grammar\" | \"spelling\" | undefined; 'aria-keyshortcuts'?: string | undefined; 'aria-labelledby'?: string | undefined; 'aria-level'?: number | undefined; 'aria-live'?: \"off\" | \"assertive\" | \"polite\" | undefined; 'aria-modal'?: Booleanish | undefined; 'aria-multiline'?: Booleanish | undefined; 'aria-multiselectable'?: Booleanish | undefined; 'aria-orientation'?: \"horizontal\" | \"vertical\" | undefined; 'aria-owns'?: string | undefined; 'aria-placeholder'?: string | undefined; 'aria-posinset'?: number | undefined; 'aria-pressed'?: boolean | \"true\" | \"false\" | \"mixed\" | undefined; 'aria-readonly'?: Booleanish | undefined; 'aria-relevant'?: \"text\" | \"additions\" | \"additions removals\" | \"additions text\" | \"all\" | \"removals\" | \"removals additions\" | \"removals text\" | \"text additions\" | \"text removals\" | undefined; 'aria-required'?: Booleanish | undefined; 'aria-roledescription'?: string | undefined; 'aria-rowcount'?: number | undefined; 'aria-rowindex'?: number | undefined; 'aria-rowspan'?: number | undefined; 'aria-selected'?: Booleanish | undefined; 'aria-setsize'?: number | undefined; 'aria-sort'?: \"none\" | \"ascending\" | \"descending\" | \"other\" | undefined; 'aria-valuemax'?: number | undefined; 'aria-valuemin'?: number | undefined; 'aria-valuenow'?: number | undefined; 'aria-valuetext'?: string | undefined; dangerouslySetInnerHTML?: { __html: string; } | undefined; onCopy?: React.ClipboardEventHandler | undefined; onCopyCapture?: React.ClipboardEventHandler | undefined; onCut?: React.ClipboardEventHandler | undefined; onCutCapture?: React.ClipboardEventHandler | undefined; onPaste?: React.ClipboardEventHandler | undefined; onPasteCapture?: React.ClipboardEventHandler | undefined; onCompositionEnd?: React.CompositionEventHandler | undefined; onCompositionEndCapture?: React.CompositionEventHandler | undefined; onCompositionStart?: React.CompositionEventHandler | undefined; onCompositionStartCapture?: React.CompositionEventHandler | undefined; onCompositionUpdate?: React.CompositionEventHandler | undefined; onCompositionUpdateCapture?: React.CompositionEventHandler | undefined; onFocus?: React.FocusEventHandler | undefined; onFocusCapture?: React.FocusEventHandler | undefined; onBlur?: React.FocusEventHandler | undefined; onBlurCapture?: React.FocusEventHandler | undefined; onChange?: ((value: string) => void) | undefined; onChangeCapture?: React.FormEventHandler | undefined; onBeforeInput?: React.FormEventHandler | undefined; onBeforeInputCapture?: React.FormEventHandler | undefined; onInput?: React.FormEventHandler | undefined; onInputCapture?: React.FormEventHandler | undefined; onReset?: React.FormEventHandler | undefined; onResetCapture?: React.FormEventHandler | undefined; onSubmit?: React.FormEventHandler | undefined; onSubmitCapture?: React.FormEventHandler | undefined; onInvalid?: React.FormEventHandler | undefined; onInvalidCapture?: React.FormEventHandler | undefined; onLoad?: React.ReactEventHandler | undefined; onLoadCapture?: React.ReactEventHandler | undefined; onError?: React.ReactEventHandler | undefined; onErrorCapture?: React.ReactEventHandler | undefined; onKeyDown?: React.KeyboardEventHandler | undefined; onKeyDownCapture?: React.KeyboardEventHandler | undefined; onKeyPress?: React.KeyboardEventHandler | undefined; onKeyPressCapture?: React.KeyboardEventHandler | undefined; onKeyUp?: React.KeyboardEventHandler | undefined; onKeyUpCapture?: React.KeyboardEventHandler | undefined; onAbort?: React.ReactEventHandler | undefined; onAbortCapture?: React.ReactEventHandler | undefined; onCanPlay?: React.ReactEventHandler | undefined; onCanPlayCapture?: React.ReactEventHandler | undefined; onCanPlayThrough?: React.ReactEventHandler | undefined; onCanPlayThroughCapture?: React.ReactEventHandler | undefined; onDurationChange?: React.ReactEventHandler | undefined; onDurationChangeCapture?: React.ReactEventHandler | undefined; onEmptied?: React.ReactEventHandler | undefined; onEmptiedCapture?: React.ReactEventHandler | undefined; onEncrypted?: React.ReactEventHandler | undefined; onEncryptedCapture?: React.ReactEventHandler | undefined; onEnded?: React.ReactEventHandler | undefined; onEndedCapture?: React.ReactEventHandler | undefined; onLoadedData?: React.ReactEventHandler | undefined; onLoadedDataCapture?: React.ReactEventHandler | undefined; onLoadedMetadata?: React.ReactEventHandler | undefined; onLoadedMetadataCapture?: React.ReactEventHandler | undefined; onLoadStart?: React.ReactEventHandler | undefined; onLoadStartCapture?: React.ReactEventHandler | undefined; onPause?: React.ReactEventHandler | undefined; onPauseCapture?: React.ReactEventHandler | undefined; onPlay?: React.ReactEventHandler | undefined; onPlayCapture?: React.ReactEventHandler | undefined; onPlaying?: React.ReactEventHandler | undefined; onPlayingCapture?: React.ReactEventHandler | undefined; onProgress?: React.ReactEventHandler | undefined; onProgressCapture?: React.ReactEventHandler | undefined; onRateChange?: React.ReactEventHandler | undefined; onRateChangeCapture?: React.ReactEventHandler | undefined; onSeeked?: React.ReactEventHandler | undefined; onSeekedCapture?: React.ReactEventHandler | undefined; onSeeking?: React.ReactEventHandler | undefined; onSeekingCapture?: React.ReactEventHandler | undefined; onStalled?: React.ReactEventHandler | undefined; onStalledCapture?: React.ReactEventHandler | undefined; onSuspend?: React.ReactEventHandler | undefined; onSuspendCapture?: React.ReactEventHandler | undefined; onTimeUpdate?: React.ReactEventHandler | undefined; onTimeUpdateCapture?: React.ReactEventHandler | undefined; onVolumeChange?: React.ReactEventHandler | undefined; onVolumeChangeCapture?: React.ReactEventHandler | undefined; onWaiting?: React.ReactEventHandler | undefined; onWaitingCapture?: React.ReactEventHandler | undefined; onAuxClick?: React.MouseEventHandler | undefined; onAuxClickCapture?: React.MouseEventHandler | undefined; onClick?: React.MouseEventHandler | undefined; onClickCapture?: React.MouseEventHandler | undefined; onContextMenu?: React.MouseEventHandler | undefined; onContextMenuCapture?: React.MouseEventHandler | undefined; onDoubleClick?: React.MouseEventHandler | undefined; onDoubleClickCapture?: React.MouseEventHandler | undefined; onDrag?: React.DragEventHandler | undefined; onDragCapture?: React.DragEventHandler | undefined; onDragEnd?: React.DragEventHandler | undefined; onDragEndCapture?: React.DragEventHandler | undefined; onDragEnter?: React.DragEventHandler | undefined; onDragEnterCapture?: React.DragEventHandler | undefined; onDragExit?: React.DragEventHandler | undefined; onDragExitCapture?: React.DragEventHandler | undefined; onDragLeave?: React.DragEventHandler | undefined; onDragLeaveCapture?: React.DragEventHandler | undefined; onDragOver?: React.DragEventHandler | undefined; onDragOverCapture?: React.DragEventHandler | undefined; onDragStart?: React.DragEventHandler | undefined; onDragStartCapture?: React.DragEventHandler | undefined; onDrop?: React.DragEventHandler | undefined; onDropCapture?: React.DragEventHandler | undefined; onMouseDown?: React.MouseEventHandler | undefined; onMouseDownCapture?: React.MouseEventHandler | undefined; onMouseEnter?: React.MouseEventHandler | undefined; onMouseLeave?: React.MouseEventHandler | undefined; onMouseMove?: React.MouseEventHandler | undefined; onMouseMoveCapture?: React.MouseEventHandler | undefined; onMouseOut?: React.MouseEventHandler | undefined; onMouseOutCapture?: React.MouseEventHandler | undefined; onMouseOver?: React.MouseEventHandler | undefined; onMouseOverCapture?: React.MouseEventHandler | undefined; onMouseUp?: React.MouseEventHandler | undefined; onMouseUpCapture?: React.MouseEventHandler | undefined; onSelect?: React.ReactEventHandler | undefined; onSelectCapture?: React.ReactEventHandler | undefined; onTouchCancel?: React.TouchEventHandler | undefined; onTouchCancelCapture?: React.TouchEventHandler | undefined; onTouchEnd?: React.TouchEventHandler | undefined; onTouchEndCapture?: React.TouchEventHandler | undefined; onTouchMove?: React.TouchEventHandler | undefined; onTouchMoveCapture?: React.TouchEventHandler | undefined; onTouchStart?: React.TouchEventHandler | undefined; onTouchStartCapture?: React.TouchEventHandler | undefined; onPointerDown?: React.PointerEventHandler | undefined; onPointerDownCapture?: React.PointerEventHandler | undefined; onPointerMove?: React.PointerEventHandler | undefined; onPointerMoveCapture?: React.PointerEventHandler | undefined; onPointerUp?: React.PointerEventHandler | undefined; onPointerUpCapture?: React.PointerEventHandler | undefined; onPointerCancel?: React.PointerEventHandler | undefined; onPointerCancelCapture?: React.PointerEventHandler | undefined; onPointerEnter?: React.PointerEventHandler | undefined; onPointerEnterCapture?: React.PointerEventHandler | undefined; onPointerLeave?: React.PointerEventHandler | undefined; onPointerLeaveCapture?: React.PointerEventHandler | undefined; onPointerOver?: React.PointerEventHandler | undefined; onPointerOverCapture?: React.PointerEventHandler | undefined; onPointerOut?: React.PointerEventHandler | undefined; onPointerOutCapture?: React.PointerEventHandler | undefined; onGotPointerCapture?: React.PointerEventHandler | undefined; onGotPointerCaptureCapture?: React.PointerEventHandler | undefined; onLostPointerCapture?: React.PointerEventHandler | undefined; onLostPointerCaptureCapture?: React.PointerEventHandler | undefined; onScroll?: React.UIEventHandler | undefined; onScrollCapture?: React.UIEventHandler | undefined; onWheel?: React.WheelEventHandler | undefined; onWheelCapture?: React.WheelEventHandler | undefined; onAnimationStart?: React.AnimationEventHandler | undefined; onAnimationStartCapture?: React.AnimationEventHandler | undefined; onAnimationEnd?: React.AnimationEventHandler | undefined; onAnimationEndCapture?: React.AnimationEventHandler | undefined; onAnimationIteration?: React.AnimationEventHandler | undefined; onAnimationIterationCapture?: React.AnimationEventHandler | undefined; onTransitionEnd?: React.TransitionEventHandler | undefined; onTransitionEndCapture?: React.TransitionEventHandler | undefined; height?: number | \"full\" | undefined; readOnly: boolean; maxHeight?: number | undefined; autoExpandPreview?: boolean | undefined; parsingPluginList?: ", + ">; defaultChecked?: boolean | undefined; suppressContentEditableWarning?: boolean | undefined; suppressHydrationWarning?: boolean | undefined; accessKey?: string | undefined; contentEditable?: Booleanish | \"inherit\" | undefined; contextMenu?: string | undefined; dir?: string | undefined; draggable?: Booleanish | undefined; hidden?: boolean | undefined; lang?: string | undefined; placeholder?: string | undefined; spellCheck?: Booleanish | undefined; tabIndex?: number | undefined; translate?: \"yes\" | \"no\" | undefined; radioGroup?: string | undefined; role?: React.AriaRole | undefined; about?: string | undefined; datatype?: string | undefined; inlist?: any; property?: string | undefined; resource?: string | undefined; typeof?: string | undefined; vocab?: string | undefined; autoCapitalize?: string | undefined; autoCorrect?: string | undefined; autoSave?: string | undefined; color?: string | undefined; itemProp?: string | undefined; itemScope?: boolean | undefined; itemType?: string | undefined; itemID?: string | undefined; itemRef?: string | undefined; results?: number | undefined; security?: string | undefined; unselectable?: \"on\" | \"off\" | undefined; inputMode?: \"search\" | \"none\" | \"text\" | \"tel\" | \"url\" | \"email\" | \"numeric\" | \"decimal\" | undefined; 'aria-activedescendant'?: string | undefined; 'aria-atomic'?: Booleanish | undefined; 'aria-autocomplete'?: \"none\" | \"list\" | \"inline\" | \"both\" | undefined; 'aria-busy'?: Booleanish | undefined; 'aria-checked'?: boolean | \"true\" | \"false\" | \"mixed\" | undefined; 'aria-colcount'?: number | undefined; 'aria-colindex'?: number | undefined; 'aria-colspan'?: number | undefined; 'aria-controls'?: string | undefined; 'aria-current'?: boolean | \"page\" | \"date\" | \"time\" | \"true\" | \"false\" | \"step\" | \"location\" | undefined; 'aria-describedby'?: string | undefined; 'aria-details'?: string | undefined; 'aria-disabled'?: Booleanish | undefined; 'aria-dropeffect'?: \"execute\" | \"link\" | \"none\" | \"copy\" | \"move\" | \"popup\" | undefined; 'aria-errormessage'?: string | undefined; 'aria-expanded'?: Booleanish | undefined; 'aria-flowto'?: string | undefined; 'aria-grabbed'?: Booleanish | undefined; 'aria-haspopup'?: boolean | \"dialog\" | \"menu\" | \"true\" | \"false\" | \"grid\" | \"listbox\" | \"tree\" | undefined; 'aria-hidden'?: Booleanish | undefined; 'aria-invalid'?: boolean | \"true\" | \"false\" | \"grammar\" | \"spelling\" | undefined; 'aria-keyshortcuts'?: string | undefined; 'aria-labelledby'?: string | undefined; 'aria-level'?: number | undefined; 'aria-live'?: \"off\" | \"assertive\" | \"polite\" | undefined; 'aria-modal'?: Booleanish | undefined; 'aria-multiline'?: Booleanish | undefined; 'aria-multiselectable'?: Booleanish | undefined; 'aria-orientation'?: \"horizontal\" | \"vertical\" | undefined; 'aria-owns'?: string | undefined; 'aria-placeholder'?: string | undefined; 'aria-posinset'?: number | undefined; 'aria-pressed'?: boolean | \"true\" | \"false\" | \"mixed\" | undefined; 'aria-readonly'?: Booleanish | undefined; 'aria-relevant'?: \"text\" | \"all\" | \"additions\" | \"additions removals\" | \"additions text\" | \"removals\" | \"removals additions\" | \"removals text\" | \"text additions\" | \"text removals\" | undefined; 'aria-required'?: Booleanish | undefined; 'aria-roledescription'?: string | undefined; 'aria-rowcount'?: number | undefined; 'aria-rowindex'?: number | undefined; 'aria-rowspan'?: number | undefined; 'aria-selected'?: Booleanish | undefined; 'aria-setsize'?: number | undefined; 'aria-sort'?: \"none\" | \"ascending\" | \"descending\" | \"other\" | undefined; 'aria-valuemax'?: number | undefined; 'aria-valuemin'?: number | undefined; 'aria-valuenow'?: number | undefined; 'aria-valuetext'?: string | undefined; dangerouslySetInnerHTML?: { __html: string; } | undefined; onCopy?: React.ClipboardEventHandler | undefined; onCopyCapture?: React.ClipboardEventHandler | undefined; onCut?: React.ClipboardEventHandler | undefined; onCutCapture?: React.ClipboardEventHandler | undefined; onPaste?: React.ClipboardEventHandler | undefined; onPasteCapture?: React.ClipboardEventHandler | undefined; onCompositionEnd?: React.CompositionEventHandler | undefined; onCompositionEndCapture?: React.CompositionEventHandler | undefined; onCompositionStart?: React.CompositionEventHandler | undefined; onCompositionStartCapture?: React.CompositionEventHandler | undefined; onCompositionUpdate?: React.CompositionEventHandler | undefined; onCompositionUpdateCapture?: React.CompositionEventHandler | undefined; onFocus?: React.FocusEventHandler | undefined; onFocusCapture?: React.FocusEventHandler | undefined; onBlur?: React.FocusEventHandler | undefined; onBlurCapture?: React.FocusEventHandler | undefined; onChange?: ((value: string) => void) | undefined; onChangeCapture?: React.FormEventHandler | undefined; onBeforeInput?: React.FormEventHandler | undefined; onBeforeInputCapture?: React.FormEventHandler | undefined; onInput?: React.FormEventHandler | undefined; onInputCapture?: React.FormEventHandler | undefined; onReset?: React.FormEventHandler | undefined; onResetCapture?: React.FormEventHandler | undefined; onSubmit?: React.FormEventHandler | undefined; onSubmitCapture?: React.FormEventHandler | undefined; onInvalid?: React.FormEventHandler | undefined; onInvalidCapture?: React.FormEventHandler | undefined; onLoad?: React.ReactEventHandler | undefined; onLoadCapture?: React.ReactEventHandler | undefined; onError?: React.ReactEventHandler | undefined; onErrorCapture?: React.ReactEventHandler | undefined; onKeyDown?: React.KeyboardEventHandler | undefined; onKeyDownCapture?: React.KeyboardEventHandler | undefined; onKeyPress?: React.KeyboardEventHandler | undefined; onKeyPressCapture?: React.KeyboardEventHandler | undefined; onKeyUp?: React.KeyboardEventHandler | undefined; onKeyUpCapture?: React.KeyboardEventHandler | undefined; onAbort?: React.ReactEventHandler | undefined; onAbortCapture?: React.ReactEventHandler | undefined; onCanPlay?: React.ReactEventHandler | undefined; onCanPlayCapture?: React.ReactEventHandler | undefined; onCanPlayThrough?: React.ReactEventHandler | undefined; onCanPlayThroughCapture?: React.ReactEventHandler | undefined; onDurationChange?: React.ReactEventHandler | undefined; onDurationChangeCapture?: React.ReactEventHandler | undefined; onEmptied?: React.ReactEventHandler | undefined; onEmptiedCapture?: React.ReactEventHandler | undefined; onEncrypted?: React.ReactEventHandler | undefined; onEncryptedCapture?: React.ReactEventHandler | undefined; onEnded?: React.ReactEventHandler | undefined; onEndedCapture?: React.ReactEventHandler | undefined; onLoadedData?: React.ReactEventHandler | undefined; onLoadedDataCapture?: React.ReactEventHandler | undefined; onLoadedMetadata?: React.ReactEventHandler | undefined; onLoadedMetadataCapture?: React.ReactEventHandler | undefined; onLoadStart?: React.ReactEventHandler | undefined; onLoadStartCapture?: React.ReactEventHandler | undefined; onPause?: React.ReactEventHandler | undefined; onPauseCapture?: React.ReactEventHandler | undefined; onPlay?: React.ReactEventHandler | undefined; onPlayCapture?: React.ReactEventHandler | undefined; onPlaying?: React.ReactEventHandler | undefined; onPlayingCapture?: React.ReactEventHandler | undefined; onProgress?: React.ReactEventHandler | undefined; onProgressCapture?: React.ReactEventHandler | undefined; onRateChange?: React.ReactEventHandler | undefined; onRateChangeCapture?: React.ReactEventHandler | undefined; onSeeked?: React.ReactEventHandler | undefined; onSeekedCapture?: React.ReactEventHandler | undefined; onSeeking?: React.ReactEventHandler | undefined; onSeekingCapture?: React.ReactEventHandler | undefined; onStalled?: React.ReactEventHandler | undefined; onStalledCapture?: React.ReactEventHandler | undefined; onSuspend?: React.ReactEventHandler | undefined; onSuspendCapture?: React.ReactEventHandler | undefined; onTimeUpdate?: React.ReactEventHandler | undefined; onTimeUpdateCapture?: React.ReactEventHandler | undefined; onVolumeChange?: React.ReactEventHandler | undefined; onVolumeChangeCapture?: React.ReactEventHandler | undefined; onWaiting?: React.ReactEventHandler | undefined; onWaitingCapture?: React.ReactEventHandler | undefined; onAuxClick?: React.MouseEventHandler | undefined; onAuxClickCapture?: React.MouseEventHandler | undefined; onClick?: React.MouseEventHandler | undefined; onClickCapture?: React.MouseEventHandler | undefined; onContextMenu?: React.MouseEventHandler | undefined; onContextMenuCapture?: React.MouseEventHandler | undefined; onDoubleClick?: React.MouseEventHandler | undefined; onDoubleClickCapture?: React.MouseEventHandler | undefined; onDrag?: React.DragEventHandler | undefined; onDragCapture?: React.DragEventHandler | undefined; onDragEnd?: React.DragEventHandler | undefined; onDragEndCapture?: React.DragEventHandler | undefined; onDragEnter?: React.DragEventHandler | undefined; onDragEnterCapture?: React.DragEventHandler | undefined; onDragExit?: React.DragEventHandler | undefined; onDragExitCapture?: React.DragEventHandler | undefined; onDragLeave?: React.DragEventHandler | undefined; onDragLeaveCapture?: React.DragEventHandler | undefined; onDragOver?: React.DragEventHandler | undefined; onDragOverCapture?: React.DragEventHandler | undefined; onDragStart?: React.DragEventHandler | undefined; onDragStartCapture?: React.DragEventHandler | undefined; onDrop?: React.DragEventHandler | undefined; onDropCapture?: React.DragEventHandler | undefined; onMouseDown?: React.MouseEventHandler | undefined; onMouseDownCapture?: React.MouseEventHandler | undefined; onMouseEnter?: React.MouseEventHandler | undefined; onMouseLeave?: React.MouseEventHandler | undefined; onMouseMove?: React.MouseEventHandler | undefined; onMouseMoveCapture?: React.MouseEventHandler | undefined; onMouseOut?: React.MouseEventHandler | undefined; onMouseOutCapture?: React.MouseEventHandler | undefined; onMouseOver?: React.MouseEventHandler | undefined; onMouseOverCapture?: React.MouseEventHandler | undefined; onMouseUp?: React.MouseEventHandler | undefined; onMouseUpCapture?: React.MouseEventHandler | undefined; onSelect?: React.ReactEventHandler | undefined; onSelectCapture?: React.ReactEventHandler | undefined; onTouchCancel?: React.TouchEventHandler | undefined; onTouchCancelCapture?: React.TouchEventHandler | undefined; onTouchEnd?: React.TouchEventHandler | undefined; onTouchEndCapture?: React.TouchEventHandler | undefined; onTouchMove?: React.TouchEventHandler | undefined; onTouchMoveCapture?: React.TouchEventHandler | undefined; onTouchStart?: React.TouchEventHandler | undefined; onTouchStartCapture?: React.TouchEventHandler | undefined; onPointerDown?: React.PointerEventHandler | undefined; onPointerDownCapture?: React.PointerEventHandler | undefined; onPointerMove?: React.PointerEventHandler | undefined; onPointerMoveCapture?: React.PointerEventHandler | undefined; onPointerUp?: React.PointerEventHandler | undefined; onPointerUpCapture?: React.PointerEventHandler | undefined; onPointerCancel?: React.PointerEventHandler | undefined; onPointerCancelCapture?: React.PointerEventHandler | undefined; onPointerEnter?: React.PointerEventHandler | undefined; onPointerEnterCapture?: React.PointerEventHandler | undefined; onPointerLeave?: React.PointerEventHandler | undefined; onPointerLeaveCapture?: React.PointerEventHandler | undefined; onPointerOver?: React.PointerEventHandler | undefined; onPointerOverCapture?: React.PointerEventHandler | undefined; onPointerOut?: React.PointerEventHandler | undefined; onPointerOutCapture?: React.PointerEventHandler | undefined; onGotPointerCapture?: React.PointerEventHandler | undefined; onGotPointerCaptureCapture?: React.PointerEventHandler | undefined; onLostPointerCapture?: React.PointerEventHandler | undefined; onLostPointerCaptureCapture?: React.PointerEventHandler | undefined; onScroll?: React.UIEventHandler | undefined; onScrollCapture?: React.UIEventHandler | undefined; onWheel?: React.WheelEventHandler | undefined; onWheelCapture?: React.WheelEventHandler | undefined; onAnimationStart?: React.AnimationEventHandler | undefined; onAnimationStartCapture?: React.AnimationEventHandler | undefined; onAnimationEnd?: React.AnimationEventHandler | undefined; onAnimationEndCapture?: React.AnimationEventHandler | undefined; onAnimationIteration?: React.AnimationEventHandler | undefined; onAnimationIterationCapture?: React.AnimationEventHandler | undefined; onTransitionEnd?: React.TransitionEventHandler | undefined; onTransitionEndCapture?: React.TransitionEventHandler | undefined; height?: number | \"full\" | undefined; readOnly: boolean; maxHeight?: number | undefined; autoExpandPreview?: boolean | undefined; parsingPluginList?: ", "PluggableList", "<", "Settings", diff --git a/api_docs/kbn_shared_ux_markdown_mocks.mdx b/api_docs/kbn_shared_ux_markdown_mocks.mdx index 105f7292deb4f..b202f9f8b5870 100644 --- a/api_docs/kbn_shared_ux_markdown_mocks.mdx +++ b/api_docs/kbn_shared_ux_markdown_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown-mocks title: "@kbn/shared-ux-markdown-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 509e2d3017b2f..156eda71b5a8c 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data title: "@kbn/shared-ux-page-analytics-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data plugin -date: 2023-02-23 +date: 2023-02-28 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 3cf6d729c5aca..0d4be286761e6 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data-mocks title: "@kbn/shared-ux-page-analytics-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 0a9c1b9e43c2f..c6f9fc4ef80ad 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data title: "@kbn/shared-ux-page-kibana-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data plugin -date: 2023-02-23 +date: 2023-02-28 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 498dfd72bba2e..ae05839415131 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data-mocks title: "@kbn/shared-ux-page-kibana-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 974910c4878a3..250d876c321e9 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template title: "@kbn/shared-ux-page-kibana-template" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template plugin -date: 2023-02-23 +date: 2023-02-28 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 a25aa5e4ba8ea..724150dcc5ac9 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template-mocks title: "@kbn/shared-ux-page-kibana-template-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 4d2ac31e70b9b..7b1b989ba6cee 100644 --- a/api_docs/kbn_shared_ux_page_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data title: "@kbn/shared-ux-page-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data plugin -date: 2023-02-23 +date: 2023-02-28 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 3ff7cfed3b9a3..5727efb34acce 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config title: "@kbn/shared-ux-page-no-data-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config plugin -date: 2023-02-23 +date: 2023-02-28 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 88f8a338dca76..c6af6866069c9 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config-mocks title: "@kbn/shared-ux-page-no-data-config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 3c3f6a9f1b854..87981ddb8777f 100644 --- a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-mocks title: "@kbn/shared-ux-page-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 b6714b6ac030d..5ba46cdcc14c4 100644 --- a/api_docs/kbn_shared_ux_page_solution_nav.mdx +++ b/api_docs/kbn_shared_ux_page_solution_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-solution-nav title: "@kbn/shared-ux-page-solution-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-solution-nav plugin -date: 2023-02-23 +date: 2023-02-28 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 ec72ee3221acb..4a0264e15a351 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views title: "@kbn/shared-ux-prompt-no-data-views" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views plugin -date: 2023-02-23 +date: 2023-02-28 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 26eb0dbe916be..f8f479bc82736 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views-mocks title: "@kbn/shared-ux-prompt-no-data-views-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 8b94c927a7f2b..e71df0240d9a5 100644 --- a/api_docs/kbn_shared_ux_prompt_not_found.mdx +++ b/api_docs/kbn_shared_ux_prompt_not_found.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-not-found title: "@kbn/shared-ux-prompt-not-found" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-not-found plugin -date: 2023-02-23 +date: 2023-02-28 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 5aa832eff4f4b..c36b9563d5951 100644 --- a/api_docs/kbn_shared_ux_router.mdx +++ b/api_docs/kbn_shared_ux_router.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router title: "@kbn/shared-ux-router" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router plugin -date: 2023-02-23 +date: 2023-02-28 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 9e3f16df9efaf..060a5d9374f60 100644 --- a/api_docs/kbn_shared_ux_router_mocks.mdx +++ b/api_docs/kbn_shared_ux_router_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router-mocks title: "@kbn/shared-ux-router-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router-mocks plugin -date: 2023-02-23 +date: 2023-02-28 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 5c4a18cccf10f..7aa8b9b5469d8 100644 --- a/api_docs/kbn_shared_ux_storybook_config.mdx +++ b/api_docs/kbn_shared_ux_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-config title: "@kbn/shared-ux-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-config plugin -date: 2023-02-23 +date: 2023-02-28 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 15a89bf4aa1d5..302f019a118b7 100644 --- a/api_docs/kbn_shared_ux_storybook_mock.mdx +++ b/api_docs/kbn_shared_ux_storybook_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-mock title: "@kbn/shared-ux-storybook-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-mock plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-mock'] --- import kbnSharedUxStorybookMockObj from './kbn_shared_ux_storybook_mock.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index dad1ef74138e7..61068adab2e94 100644 --- a/api_docs/kbn_shared_ux_utility.mdx +++ b/api_docs/kbn_shared_ux_utility.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-utility title: "@kbn/shared-ux-utility" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-utility plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility'] --- import kbnSharedUxUtilityObj from './kbn_shared_ux_utility.devdocs.json'; diff --git a/api_docs/kbn_slo_schema.mdx b/api_docs/kbn_slo_schema.mdx index 1fb176cb13c62..a725bef6d1dfd 100644 --- a/api_docs/kbn_slo_schema.mdx +++ b/api_docs/kbn_slo_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-slo-schema title: "@kbn/slo-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/slo-schema plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/slo-schema'] --- import kbnSloSchemaObj from './kbn_slo_schema.devdocs.json'; diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index 06ab53cc5e0c0..26d5e2905557a 100644 --- a/api_docs/kbn_some_dev_log.mdx +++ b/api_docs/kbn_some_dev_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-some-dev-log title: "@kbn/some-dev-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/some-dev-log plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/some-dev-log'] --- import kbnSomeDevLogObj from './kbn_some_dev_log.devdocs.json'; diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index ab75032e681eb..ebaaad4fe200f 100644 --- a/api_docs/kbn_std.mdx +++ b/api_docs/kbn_std.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-std title: "@kbn/std" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/std plugin -date: 2023-02-23 +date: 2023-02-28 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 1faf4d8ae1cce..bc9fc88083646 100644 --- a/api_docs/kbn_stdio_dev_helpers.mdx +++ b/api_docs/kbn_stdio_dev_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-stdio-dev-helpers title: "@kbn/stdio-dev-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/stdio-dev-helpers plugin -date: 2023-02-23 +date: 2023-02-28 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 799a2385e8875..8d729da1d0c13 100644 --- a/api_docs/kbn_storybook.mdx +++ b/api_docs/kbn_storybook.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-storybook title: "@kbn/storybook" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/storybook plugin -date: 2023-02-23 +date: 2023-02-28 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 2a369548be327..af4f7c3196754 100644 --- a/api_docs/kbn_telemetry_tools.mdx +++ b/api_docs/kbn_telemetry_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-telemetry-tools title: "@kbn/telemetry-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/telemetry-tools plugin -date: 2023-02-23 +date: 2023-02-28 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 27130e51882ee..8ac5a54019cef 100644 --- a/api_docs/kbn_test.mdx +++ b/api_docs/kbn_test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test title: "@kbn/test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test'] --- import kbnTestObj from './kbn_test.devdocs.json'; diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx index 4fb7b2b4fe13c..2e5cc062ce04f 100644 --- a/api_docs/kbn_test_jest_helpers.mdx +++ b/api_docs/kbn_test_jest_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-jest-helpers title: "@kbn/test-jest-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-jest-helpers plugin -date: 2023-02-23 +date: 2023-02-28 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 19895b67e57bf..01ab9a6025585 100644 --- a/api_docs/kbn_test_subj_selector.mdx +++ b/api_docs/kbn_test_subj_selector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-subj-selector title: "@kbn/test-subj-selector" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-subj-selector plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-subj-selector'] --- import kbnTestSubjSelectorObj from './kbn_test_subj_selector.devdocs.json'; diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index 218c6eec4e0e4..b87d54e58c159 100644 --- a/api_docs/kbn_tooling_log.mdx +++ b/api_docs/kbn_tooling_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-tooling-log title: "@kbn/tooling-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/tooling-log plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] --- import kbnToolingLogObj from './kbn_tooling_log.devdocs.json'; diff --git a/api_docs/kbn_ts_projects.mdx b/api_docs/kbn_ts_projects.mdx index d5abd1d10845c..db3164da08dfc 100644 --- a/api_docs/kbn_ts_projects.mdx +++ b/api_docs/kbn_ts_projects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ts-projects title: "@kbn/ts-projects" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ts-projects plugin -date: 2023-02-23 +date: 2023-02-28 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 748986fb399e3..458810012a7dd 100644 --- a/api_docs/kbn_typed_react_router_config.mdx +++ b/api_docs/kbn_typed_react_router_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-typed-react-router-config title: "@kbn/typed-react-router-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/typed-react-router-config plugin -date: 2023-02-23 +date: 2023-02-28 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 9c4a6d9f0c6c0..d58966203073b 100644 --- a/api_docs/kbn_ui_actions_browser.mdx +++ b/api_docs/kbn_ui_actions_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-actions-browser title: "@kbn/ui-actions-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-actions-browser plugin -date: 2023-02-23 +date: 2023-02-28 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 b492fd11a621c..68704c5518edc 100644 --- a/api_docs/kbn_ui_shared_deps_src.mdx +++ b/api_docs/kbn_ui_shared_deps_src.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-shared-deps-src title: "@kbn/ui-shared-deps-src" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-shared-deps-src plugin -date: 2023-02-23 +date: 2023-02-28 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 a3a3625809478..125fb40ae685c 100644 --- a/api_docs/kbn_ui_theme.mdx +++ b/api_docs/kbn_ui_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-theme title: "@kbn/ui-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-theme plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] --- import kbnUiThemeObj from './kbn_ui_theme.devdocs.json'; diff --git a/api_docs/kbn_user_profile_components.mdx b/api_docs/kbn_user_profile_components.mdx index c9840bb0b8911..6e7c773415301 100644 --- a/api_docs/kbn_user_profile_components.mdx +++ b/api_docs/kbn_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-user-profile-components title: "@kbn/user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/user-profile-components plugin -date: 2023-02-23 +date: 2023-02-28 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 806b0cbe617ae..0c7dc2488443a 100644 --- a/api_docs/kbn_utility_types.mdx +++ b/api_docs/kbn_utility_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types title: "@kbn/utility-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types plugin -date: 2023-02-23 +date: 2023-02-28 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 d1c66f11244ab..0d8c7b33c2bd6 100644 --- a/api_docs/kbn_utility_types_jest.mdx +++ b/api_docs/kbn_utility_types_jest.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types-jest title: "@kbn/utility-types-jest" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types-jest plugin -date: 2023-02-23 +date: 2023-02-28 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 504217590492a..3e65e26219007 100644 --- a/api_docs/kbn_utils.mdx +++ b/api_docs/kbn_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utils title: "@kbn/utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utils plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] --- import kbnUtilsObj from './kbn_utils.devdocs.json'; diff --git a/api_docs/kbn_yarn_lock_validator.mdx b/api_docs/kbn_yarn_lock_validator.mdx index 484a50fc88391..f78260916241f 100644 --- a/api_docs/kbn_yarn_lock_validator.mdx +++ b/api_docs/kbn_yarn_lock_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-yarn-lock-validator title: "@kbn/yarn-lock-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/yarn-lock-validator plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/yarn-lock-validator'] --- import kbnYarnLockValidatorObj from './kbn_yarn_lock_validator.devdocs.json'; diff --git a/api_docs/kibana_overview.mdx b/api_docs/kibana_overview.mdx index 93598f2a843f5..6c0fb6464f98c 100644 --- a/api_docs/kibana_overview.mdx +++ b/api_docs/kibana_overview.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaOverview title: "kibanaOverview" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaOverview plugin -date: 2023-02-23 +date: 2023-02-28 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 c03b43abd3ed8..f381f2825210d 100644 --- a/api_docs/kibana_react.mdx +++ b/api_docs/kibana_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaReact title: "kibanaReact" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaReact plugin -date: 2023-02-23 +date: 2023-02-28 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 da36c5b5bc582..58fc492ba9ca5 100644 --- a/api_docs/kibana_utils.mdx +++ b/api_docs/kibana_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaUtils title: "kibanaUtils" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaUtils plugin -date: 2023-02-23 +date: 2023-02-28 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 8fc0291de0a5f..dccc3f7001254 100644 --- a/api_docs/kubernetes_security.mdx +++ b/api_docs/kubernetes_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kubernetesSecurity title: "kubernetesSecurity" image: https://source.unsplash.com/400x175/?github description: API docs for the kubernetesSecurity plugin -date: 2023-02-23 +date: 2023-02-28 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 8cb25d878c1fb..c33e99100df1e 100644 --- a/api_docs/lens.mdx +++ b/api_docs/lens.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lens title: "lens" image: https://source.unsplash.com/400x175/?github description: API docs for the lens plugin -date: 2023-02-23 +date: 2023-02-28 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 ab160c34e66c0..23daa345f6194 100644 --- a/api_docs/license_api_guard.mdx +++ b/api_docs/license_api_guard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseApiGuard title: "licenseApiGuard" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseApiGuard plugin -date: 2023-02-23 +date: 2023-02-28 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 c1b5cf7f90d07..2d3259a4aee8b 100644 --- a/api_docs/license_management.mdx +++ b/api_docs/license_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseManagement title: "licenseManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseManagement plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseManagement'] --- import licenseManagementObj from './license_management.devdocs.json'; diff --git a/api_docs/licensing.devdocs.json b/api_docs/licensing.devdocs.json index 2d6936944a2d3..f83a3f0c03d25 100644 --- a/api_docs/licensing.devdocs.json +++ b/api_docs/licensing.devdocs.json @@ -2172,7 +2172,7 @@ }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/query.ts" + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/query.ts" }, { "plugin": "snapshotRestore", diff --git a/api_docs/licensing.mdx b/api_docs/licensing.mdx index 26f2e4ec2c55e..3038e387bec74 100644 --- a/api_docs/licensing.mdx +++ b/api_docs/licensing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licensing title: "licensing" image: https://source.unsplash.com/400x175/?github description: API docs for the licensing plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licensing'] --- import licensingObj from './licensing.devdocs.json'; diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index aa32e1365cd60..d25c61a4e27c9 100644 --- a/api_docs/lists.mdx +++ b/api_docs/lists.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lists title: "lists" image: https://source.unsplash.com/400x175/?github description: API docs for the lists plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; diff --git a/api_docs/management.mdx b/api_docs/management.mdx index 90e1f842818ab..d416a6004f48d 100644 --- a/api_docs/management.mdx +++ b/api_docs/management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/management title: "management" image: https://source.unsplash.com/400x175/?github description: API docs for the management plugin -date: 2023-02-23 +date: 2023-02-28 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 d2cd63b8b17d5..18079ba00f748 100644 --- a/api_docs/maps.mdx +++ b/api_docs/maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/maps title: "maps" image: https://source.unsplash.com/400x175/?github description: API docs for the maps plugin -date: 2023-02-23 +date: 2023-02-28 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 377301af84818..4615a906a1cd7 100644 --- a/api_docs/maps_ems.mdx +++ b/api_docs/maps_ems.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/mapsEms title: "mapsEms" image: https://source.unsplash.com/400x175/?github description: API docs for the mapsEms plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] --- import mapsEmsObj from './maps_ems.devdocs.json'; diff --git a/api_docs/ml.devdocs.json b/api_docs/ml.devdocs.json index e74cc17d3cb82..6d4c171c7664b 100644 --- a/api_docs/ml.devdocs.json +++ b/api_docs/ml.devdocs.json @@ -296,6 +296,39 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "ml", + "id": "def-public.MlNodeAvailableWarningShared", + "type": "Function", + "tags": [], + "label": "MlNodeAvailableWarningShared", + "description": [], + "signature": [ + "({ nodeAvailableCallback, size }: React.PropsWithChildren) => JSX.Element | null" + ], + "path": "x-pack/plugins/ml/public/application/components/node_available_warning/node_available_warning_shared.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "ml", + "id": "def-public.MlNodeAvailableWarningShared.$1", + "type": "CompoundType", + "tags": [], + "label": "{ nodeAvailableCallback, size }", + "description": [], + "signature": [ + "React.PropsWithChildren" + ], + "path": "x-pack/plugins/ml/public/application/components/node_available_warning/node_available_warning_shared.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "ml", "id": "def-public.useMlHref", @@ -381,6 +414,23 @@ ], "returnComment": [], "initialIsOpen": false + }, + { + "parentPluginId": "ml", + "id": "def-public.useMlNodeAvailableCheck", + "type": "Function", + "tags": [], + "label": "useMlNodeAvailableCheck", + "description": [], + "signature": [ + "() => { mlNodesAvailable: boolean; isCloud: boolean; deploymentId: string | null; isCloudTrial: boolean; }" + ], + "path": "x-pack/plugins/ml/public/application/components/node_available_warning/hooks.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [], + "initialIsOpen": false } ], "interfaces": [ diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index cda42544913e8..fbfca60b77b13 100644 --- a/api_docs/ml.mdx +++ b/api_docs/ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ml title: "ml" image: https://source.unsplash.com/400x175/?github description: API docs for the ml plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ml'] --- import mlObj from './ml.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) for questi | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 256 | 9 | 80 | 40 | +| 259 | 9 | 83 | 40 | ## Client diff --git a/api_docs/monitoring.mdx b/api_docs/monitoring.mdx index da0f1b44a562b..886a4ce48b798 100644 --- a/api_docs/monitoring.mdx +++ b/api_docs/monitoring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoring title: "monitoring" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoring plugin -date: 2023-02-23 +date: 2023-02-28 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 69840d14ba9ae..f3b8c8d1be282 100644 --- a/api_docs/monitoring_collection.mdx +++ b/api_docs/monitoring_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoringCollection title: "monitoringCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoringCollection plugin -date: 2023-02-23 +date: 2023-02-28 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 4f3d9e9d976bb..838c42c39dbb3 100644 --- a/api_docs/navigation.mdx +++ b/api_docs/navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/navigation title: "navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the navigation plugin -date: 2023-02-23 +date: 2023-02-28 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 8ba8ae3d82156..75c74e1f19b30 100644 --- a/api_docs/newsfeed.mdx +++ b/api_docs/newsfeed.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/newsfeed title: "newsfeed" image: https://source.unsplash.com/400x175/?github description: API docs for the newsfeed plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/notifications.mdx b/api_docs/notifications.mdx index 84120c96f507e..4d8591294aa9b 100644 --- a/api_docs/notifications.mdx +++ b/api_docs/notifications.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/notifications title: "notifications" image: https://source.unsplash.com/400x175/?github description: API docs for the notifications plugin -date: 2023-02-23 +date: 2023-02-28 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 cfb4a4186999f..15c641bc7406a 100644 --- a/api_docs/observability.devdocs.json +++ b/api_docs/observability.devdocs.json @@ -4679,7 +4679,7 @@ "label": "format", "description": [], "signature": [ - "(options: { fields: OutputOf> & Record; formatters: { asDuration: (value: ", + "(options: { fields: OutputOf> & Record; formatters: { asDuration: (value: ", "Maybe", ", { defaultValue, extended }?: FormatterOptions) => string; asPercent: (numerator: ", "Maybe", @@ -4698,7 +4698,7 @@ "label": "options", "description": [], "signature": [ - "{ fields: OutputOf> & Record; formatters: { asDuration: (value: ", + "{ fields: OutputOf> & Record; formatters: { asDuration: (value: ", "Maybe", ", { defaultValue, extended }?: FormatterOptions) => string; asPercent: (numerator: ", "Maybe", @@ -6236,7 +6236,7 @@ "label": "ObservabilityRuleTypeFormatter", "description": [], "signature": [ - "(options: { fields: OutputOf> & Record; formatters: { asDuration: (value: ", + "(options: { fields: OutputOf> & Record; formatters: { asDuration: (value: ", "Maybe", ", { defaultValue, extended }?: FormatterOptions) => string; asPercent: (numerator: ", "Maybe", @@ -6255,7 +6255,7 @@ "label": "options", "description": [], "signature": [ - "{ fields: OutputOf> & Record; formatters: { asDuration: (value: ", + "{ fields: OutputOf> & Record; formatters: { asDuration: (value: ", "Maybe", ", { defaultValue, extended }?: FormatterOptions) => string; asPercent: (numerator: ", "Maybe", @@ -9805,97 +9805,6 @@ "deprecated": false, "trackAdoption": false, "children": [ - { - "parentPluginId": "observability", - "id": "def-server.ObservabilityRouteHandlerResources.core", - "type": "Object", - "tags": [], - "label": "core", - "description": [], - "signature": [ - "{ start: () => Promise<", - { - "pluginId": "@kbn/core-lifecycle-server", - "scope": "common", - "docId": "kibKbnCoreLifecycleServerPluginApi", - "section": "def-common.CoreStart", - "text": "CoreStart" - }, - ">; setup: ", - { - "pluginId": "@kbn/core-lifecycle-server", - "scope": "common", - "docId": "kibKbnCoreLifecycleServerPluginApi", - "section": "def-common.CoreSetup", - "text": "CoreSetup" - }, - "; }" - ], - "path": "x-pack/plugins/observability/server/routes/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "observability", - "id": "def-server.ObservabilityRouteHandlerResources.ruleDataService", - "type": "Object", - "tags": [], - "label": "ruleDataService", - "description": [], - "signature": [ - { - "pluginId": "ruleRegistry", - "scope": "server", - "docId": "kibRuleRegistryPluginApi", - "section": "def-server.IRuleDataService", - "text": "IRuleDataService" - } - ], - "path": "x-pack/plugins/observability/server/routes/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "observability", - "id": "def-server.ObservabilityRouteHandlerResources.spacesService", - "type": "Object", - "tags": [], - "label": "spacesService", - "description": [], - "signature": [ - { - "pluginId": "spaces", - "scope": "server", - "docId": "kibSpacesPluginApi", - "section": "def-server.SpacesServiceStart", - "text": "SpacesServiceStart" - } - ], - "path": "x-pack/plugins/observability/server/routes/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "observability", - "id": "def-server.ObservabilityRouteHandlerResources.request", - "type": "Object", - "tags": [], - "label": "request", - "description": [], - "signature": [ - { - "pluginId": "@kbn/core-http-server", - "scope": "common", - "docId": "kibKbnCoreHttpServerPluginApi", - "section": "def-common.KibanaRequest", - "text": "KibanaRequest" - }, - "" - ], - "path": "x-pack/plugins/observability/server/routes/types.ts", - "deprecated": false, - "trackAdoption": false - }, { "parentPluginId": "observability", "id": "def-server.ObservabilityRouteHandlerResources.context", @@ -9941,6 +9850,20 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "observability", + "id": "def-server.ObservabilityRouteHandlerResources.dependencies", + "type": "Object", + "tags": [], + "label": "dependencies", + "description": [], + "signature": [ + "RegisterRoutesDependencies" + ], + "path": "x-pack/plugins/observability/server/routes/types.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "observability", "id": "def-server.ObservabilityRouteHandlerResources.logger", @@ -9960,6 +9883,27 @@ "path": "x-pack/plugins/observability/server/routes/types.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "observability", + "id": "def-server.ObservabilityRouteHandlerResources.request", + "type": "Object", + "tags": [], + "label": "request", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + "" + ], + "path": "x-pack/plugins/observability/server/routes/types.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index 8233da03a4ace..d2af532fc95fe 100644 --- a/api_docs/observability.mdx +++ b/api_docs/observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observability title: "observability" image: https://source.unsplash.com/400x175/?github description: API docs for the observability plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/actionable-observability](https://github.com/orgs/elastic/team | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 627 | 43 | 621 | 33 | +| 625 | 43 | 619 | 34 | ## Client diff --git a/api_docs/osquery.devdocs.json b/api_docs/osquery.devdocs.json index 29b2b564836a6..6232998bcc0ea 100644 --- a/api_docs/osquery.devdocs.json +++ b/api_docs/osquery.devdocs.json @@ -262,7 +262,7 @@ "label": "osqueryCreateAction", "description": [], "signature": [ - "(payload: { agent_ids?: string[] | undefined; agent_all?: boolean | undefined; agent_platforms?: string[] | undefined; agent_policy_ids?: string[] | undefined; query?: string | undefined; queries?: { id: string; query: string; ecs_mapping: { [x: string]: { field?: string | undefined; value?: string | string[] | undefined; }; } | undefined; version: string | undefined; platform: string | undefined; removed: boolean | undefined; snapshot: boolean | undefined; }[] | undefined; saved_query_id?: string | undefined; ecs_mapping?: { [x: string]: { field?: string | undefined; value?: string | string[] | undefined; }; } | undefined; pack_id?: string | undefined; alert_ids?: string[] | undefined; case_ids?: string[] | undefined; event_ids?: string[] | undefined; metadata?: object | undefined; }, alertData?: OutputOf> | undefined) => void" + "(payload: { agent_ids?: string[] | undefined; agent_all?: boolean | undefined; agent_platforms?: string[] | undefined; agent_policy_ids?: string[] | undefined; query?: string | undefined; queries?: { id: string; query: string; ecs_mapping: { [x: string]: { field?: string | undefined; value?: string | string[] | undefined; }; } | undefined; version: string | undefined; platform: string | undefined; removed: boolean | undefined; snapshot: boolean | undefined; }[] | undefined; saved_query_id?: string | undefined; ecs_mapping?: { [x: string]: { field?: string | undefined; value?: string | string[] | undefined; }; } | undefined; pack_id?: string | undefined; alert_ids?: string[] | undefined; case_ids?: string[] | undefined; event_ids?: string[] | undefined; metadata?: object | undefined; }, alertData?: OutputOf> | undefined) => void" ], "path": "x-pack/plugins/osquery/server/types.ts", "deprecated": false, @@ -291,7 +291,7 @@ "label": "alertData", "description": [], "signature": [ - "OutputOf> | undefined" + "OutputOf> | undefined" ], "path": "x-pack/plugins/osquery/server/types.ts", "deprecated": false, diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index 5d74dbbddd768..04f0bad23e2fe 100644 --- a/api_docs/osquery.mdx +++ b/api_docs/osquery.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/osquery title: "osquery" image: https://source.unsplash.com/400x175/?github description: API docs for the osquery plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery'] --- import osqueryObj from './osquery.devdocs.json'; diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index 0e093852e1e01..e548f18220b28 100644 --- a/api_docs/plugin_directory.mdx +++ b/api_docs/plugin_directory.mdx @@ -7,7 +7,7 @@ id: kibDevDocsPluginDirectory slug: /kibana-dev-docs/api-meta/plugin-api-directory title: Directory description: Directory of public APIs available through plugins or packages. -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -15,13 +15,13 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Count | Plugins or Packages with a
public API | Number of teams | |--------------|----------|------------------------| -| 574 | 469 | 38 | +| 576 | 472 | 38 | ### Public API health stats | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 67707 | 514 | 58537 | 1235 | +| 67775 | 514 | 58529 | 1233 | ## Plugin Directory @@ -30,7 +30,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 256 | 8 | 251 | 24 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 36 | 1 | 32 | 2 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | AIOps plugin maintained by ML team. | 12 | 0 | 1 | 2 | -| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 487 | 1 | 476 | 40 | +| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 497 | 1 | 486 | 41 | | | [@elastic/apm-ui](https://github.com/orgs/elastic/teams/apm-ui) | The user interface for Elastic APM | 42 | 0 | 42 | 65 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 9 | 0 | 9 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Considering using bfetch capabilities when fetching large amounts of data. This services supports batching HTTP requests and streaming responses back. | 89 | 1 | 74 | 2 | @@ -48,7 +48,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-cloud-security-posture](https://github.com/orgs/elastic/teams/kibana-cloud-security-posture) | The cloud security posture plugin | 17 | 0 | 2 | 2 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 13 | 0 | 13 | 1 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Content management app | 46 | 0 | 46 | 3 | -| | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | The Controls Plugin contains embeddable components intended to create a simple query interface for end users, and a powerful editing suite that allows dashboard authors to build controls | 270 | 0 | 266 | 9 | +| | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | The Controls Plugin contains embeddable components intended to create a simple query interface for end users, and a powerful editing suite that allows dashboard authors to build controls | 272 | 0 | 268 | 11 | | crossClusterReplication | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 0 | 0 | 0 | 0 | | customBranding | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Enables customization of Kibana | 0 | 0 | 0 | 0 | | | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | Add custom data integrations so they can be displayed in the Fleet integrations app | 107 | 0 | 88 | 1 | @@ -88,9 +88,9 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 236 | 0 | 100 | 2 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Index pattern fields and ambiguous values formatters | 288 | 26 | 249 | 3 | | | [@elastic/kibana-gis](https://github.com/orgs/elastic/teams/kibana-gis) | The file upload plugin contains components and services for uploading a file, analyzing its data, and then importing the data into an Elasticsearch index. Supported file types include CSV, TSV, newline-delimited JSON and GeoJSON. | 62 | 0 | 62 | 2 | -| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | File upload, download, sharing, and serving over HTTP implementation in Kibana. | 254 | 1 | 45 | 5 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | File upload, download, sharing, and serving over HTTP implementation in Kibana. | 214 | 0 | 10 | 5 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Simple UI for managing files in Kibana | 2 | 1 | 2 | 0 | -| | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1087 | 3 | 982 | 27 | +| | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1089 | 3 | 984 | 27 | | ftrApis | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 68 | 0 | 14 | 5 | | globalSearchBar | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 0 | 0 | 0 | 0 | @@ -121,16 +121,16 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 41 | 0 | 41 | 6 | | | [@elastic/kibana-gis](https://github.com/orgs/elastic/teams/kibana-gis) | - | 267 | 0 | 266 | 27 | | | [@elastic/kibana-gis](https://github.com/orgs/elastic/teams/kibana-gis) | - | 67 | 0 | 67 | 0 | -| | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | This plugin provides access to the machine learning features provided by Elastic. | 256 | 9 | 80 | 40 | +| | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | This plugin provides access to the machine learning features provided by Elastic. | 259 | 9 | 83 | 40 | | | [@elastic/infra-monitoring-ui](https://github.com/orgs/elastic/teams/infra-monitoring-ui) | - | 15 | 3 | 13 | 1 | | | [@elastic/infra-monitoring-ui](https://github.com/orgs/elastic/teams/infra-monitoring-ui) | - | 9 | 0 | 9 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 34 | 0 | 34 | 2 | | | [@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) | - | 2 | 0 | 2 | 1 | -| | [@elastic/actionable-observability](https://github.com/orgs/elastic/teams/actionable-observability) | - | 627 | 43 | 621 | 33 | +| | [@elastic/actionable-observability](https://github.com/orgs/elastic/teams/actionable-observability) | - | 625 | 43 | 619 | 34 | | | [@elastic/security-defend-workflows](https://github.com/orgs/elastic/teams/security-defend-workflows) | - | 24 | 0 | 24 | 7 | | painlessLab | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 0 | 0 | 0 | 0 | -| | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | The Presentation Utility Plugin is a set of common, shared components and toolkits for solutions within the Presentation space, (e.g. Dashboards, Canvas). | 219 | 7 | 163 | 12 | +| | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | The Presentation Utility Plugin is a set of common, shared components and toolkits for solutions within the Presentation space, (e.g. Dashboards, Canvas). | 202 | 7 | 146 | 12 | | | [@elastic/profiling-ui](https://github.com/orgs/elastic/teams/profiling-ui) | - | 15 | 2 | 15 | 0 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 4 | 0 | 4 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Reporting Services enables applications to feature reports that the user can automate with Watcher and download later. | 36 | 0 | 16 | 0 | @@ -199,6 +199,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 6 | 0 | 6 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 53 | 0 | 22 | 0 | | | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | - | 9 | 1 | 9 | 0 | +| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 12 | 0 | 12 | 0 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 5 | 0 | 4 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 73 | 0 | 73 | 2 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 98 | 0 | 0 | 0 | @@ -295,9 +296,9 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 22 | 0 | 7 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 9 | 0 | 9 | 3 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 7 | 0 | 7 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 25 | 5 | 25 | 1 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 26 | 6 | 26 | 1 | | | [@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) | - | 403 | 1 | 160 | 0 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 411 | 1 | 160 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 55 | 0 | 49 | 6 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 41 | 0 | 40 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 2 | 0 | @@ -323,8 +324,8 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 6 | 0 | 6 | 0 | | | [@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) | - | 6 | 0 | 0 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 5 | 0 | 0 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 7 | 0 | 6 | 1 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 6 | 0 | 0 | 0 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 3 | 1 | | | [@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) | - | 35 | 4 | 23 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 32 | 0 | 11 | 2 | @@ -341,7 +342,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 2 | 0 | 2 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 2 | 0 | 2 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 4 | 1 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 23 | 1 | 22 | 0 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 25 | 1 | 24 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 107 | 1 | 0 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 334 | 1 | 4 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 75 | 0 | 54 | 1 | @@ -354,7 +355,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 71 | 0 | 39 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 25 | 0 | 23 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 4 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 112 | 0 | 79 | 45 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 116 | 0 | 81 | 46 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 12 | 0 | 12 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 489 | 1 | 98 | 4 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 69 | 0 | 69 | 4 | @@ -402,6 +403,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 251 | 1 | 193 | 15 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 12 | 0 | 12 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 2 | 0 | 1 | 0 | +| | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | - | 13 | 0 | 4 | 3 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 20 | 0 | 16 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 2 | 0 | 0 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 29 | 0 | 29 | 1 | @@ -472,7 +474,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 40 | 0 | 3 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 8 | 0 | 4 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 13 | 0 | 9 | 0 | -| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 25 | 0 | 8 | 0 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 27 | 0 | 10 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 10 | 0 | 4 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 32 | 0 | 28 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 5 | 0 | 4 | 0 | @@ -480,6 +482,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 2 | 0 | 2 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 1 | 0 | 1 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 14 | 0 | 6 | 0 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 70 | 0 | 9 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 7 | 0 | 7 | 1 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 17 | 0 | 15 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 17 | 0 | 9 | 0 | diff --git a/api_docs/presentation_util.devdocs.json b/api_docs/presentation_util.devdocs.json index 7f53e919c56ab..6874d21e658ae 100644 --- a/api_docs/presentation_util.devdocs.json +++ b/api_docs/presentation_util.devdocs.json @@ -568,41 +568,6 @@ } ], "functions": [ - { - "parentPluginId": "presentationUtil", - "id": "def-public.AddFromLibraryButton", - "type": "Function", - "tags": [], - "label": "AddFromLibraryButton", - "description": [], - "signature": [ - "({ onClick, ...rest }: ", - "Props", - ") => JSX.Element" - ], - "path": "src/plugins/presentation_util/public/components/solution_toolbar/items/add_from_library.tsx", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "presentationUtil", - "id": "def-public.AddFromLibraryButton.$1", - "type": "Object", - "tags": [], - "label": "{ onClick, ...rest }", - "description": [], - "signature": [ - "Props" - ], - "path": "src/plugins/presentation_util/public/components/solution_toolbar/items/add_from_library.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, { "parentPluginId": "presentationUtil", "id": "def-public.cleanFiltersForSerialize", @@ -1038,113 +1003,6 @@ ], "initialIsOpen": false }, - { - "parentPluginId": "presentationUtil", - "id": "def-public.PrimaryActionButton", - "type": "Function", - "tags": [], - "label": "PrimaryActionButton", - "description": [], - "signature": [ - "({ isDarkModeEnabled, ...props }: ", - "Props", - ") => JSX.Element" - ], - "path": "src/plugins/presentation_util/public/components/solution_toolbar/items/primary_button.tsx", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "presentationUtil", - "id": "def-public.PrimaryActionButton.$1", - "type": "Object", - "tags": [], - "label": "{ isDarkModeEnabled, ...props }", - "description": [], - "signature": [ - "Props" - ], - "path": "src/plugins/presentation_util/public/components/solution_toolbar/items/primary_button.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "presentationUtil", - "id": "def-public.PrimaryActionPopover", - "type": "Function", - "tags": [], - "label": "PrimaryActionPopover", - "description": [], - "signature": [ - "(props: Omit<", - "Props", - ", \"primary\">) => JSX.Element" - ], - "path": "src/plugins/presentation_util/public/components/solution_toolbar/items/primary_popover.tsx", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "presentationUtil", - "id": "def-public.PrimaryActionPopover.$1", - "type": "Object", - "tags": [], - "label": "props", - "description": [], - "signature": [ - "Omit<", - "Props", - ", \"primary\">" - ], - "path": "src/plugins/presentation_util/public/components/solution_toolbar/items/primary_popover.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "presentationUtil", - "id": "def-public.QuickButtonGroup", - "type": "Function", - "tags": [], - "label": "QuickButtonGroup", - "description": [], - "signature": [ - "({ buttons }: ", - "Props", - ") => JSX.Element" - ], - "path": "src/plugins/presentation_util/public/components/solution_toolbar/items/quick_group.tsx", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "presentationUtil", - "id": "def-public.QuickButtonGroup.$1", - "type": "Object", - "tags": [], - "label": "{ buttons }", - "description": [], - "signature": [ - "Props" - ], - "path": "src/plugins/presentation_util/public/components/solution_toolbar/items/quick_group.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, { "parentPluginId": "presentationUtil", "id": "def-public.registerExpressionsLanguage", @@ -1197,111 +1055,6 @@ "returnComment": [], "initialIsOpen": false }, - { - "parentPluginId": "presentationUtil", - "id": "def-public.SolutionToolbar", - "type": "Function", - "tags": [], - "label": "SolutionToolbar", - "description": [], - "signature": [ - "({ isDarkModeEnabled, children }: ", - "Props", - ") => JSX.Element" - ], - "path": "src/plugins/presentation_util/public/components/solution_toolbar/solution_toolbar.tsx", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "presentationUtil", - "id": "def-public.SolutionToolbar.$1", - "type": "Object", - "tags": [], - "label": "{ isDarkModeEnabled, children }", - "description": [], - "signature": [ - "Props" - ], - "path": "src/plugins/presentation_util/public/components/solution_toolbar/solution_toolbar.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "presentationUtil", - "id": "def-public.SolutionToolbarButton", - "type": "Function", - "tags": [], - "label": "SolutionToolbarButton", - "description": [], - "signature": [ - "({ label, primary, className, ...rest }: ", - "Props", - ") => JSX.Element" - ], - "path": "src/plugins/presentation_util/public/components/solution_toolbar/items/button.tsx", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "presentationUtil", - "id": "def-public.SolutionToolbarButton.$1", - "type": "Object", - "tags": [], - "label": "{ label, primary, className, ...rest }", - "description": [], - "signature": [ - "Props" - ], - "path": "src/plugins/presentation_util/public/components/solution_toolbar/items/button.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "presentationUtil", - "id": "def-public.SolutionToolbarPopover", - "type": "Function", - "tags": [], - "label": "SolutionToolbarPopover", - "description": [], - "signature": [ - "({ label, iconType, primary, iconSide, children, ...popover }: ", - "Props", - ") => JSX.Element" - ], - "path": "src/plugins/presentation_util/public/components/solution_toolbar/items/popover.tsx", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "presentationUtil", - "id": "def-public.SolutionToolbarPopover.$1", - "type": "CompoundType", - "tags": [], - "label": "{\n label,\n iconType,\n primary,\n iconSide,\n children,\n ...popover\n}", - "description": [], - "signature": [ - "Props" - ], - "path": "src/plugins/presentation_util/public/components/solution_toolbar/items/popover.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, { "parentPluginId": "presentationUtil", "id": "def-public.useLabs", @@ -2158,59 +1911,6 @@ ], "initialIsOpen": false }, - { - "parentPluginId": "presentationUtil", - "id": "def-public.QuickButtonProps", - "type": "Interface", - "tags": [], - "label": "QuickButtonProps", - "description": [], - "signature": [ - { - "pluginId": "presentationUtil", - "scope": "public", - "docId": "kibPresentationUtilPluginApi", - "section": "def-public.QuickButtonProps", - "text": "QuickButtonProps" - }, - " extends Pick<", - "EuiButtonGroupOptionProps", - ", \"iconType\">" - ], - "path": "src/plugins/presentation_util/public/components/solution_toolbar/items/quick_group.tsx", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "presentationUtil", - "id": "def-public.QuickButtonProps.createType", - "type": "string", - "tags": [], - "label": "createType", - "description": [], - "path": "src/plugins/presentation_util/public/components/solution_toolbar/items/quick_group.tsx", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "presentationUtil", - "id": "def-public.QuickButtonProps.onClick", - "type": "Function", - "tags": [], - "label": "onClick", - "description": [], - "signature": [ - "() => void" - ], - "path": "src/plugins/presentation_util/public/components/solution_toolbar/items/quick_group.tsx", - "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [] - } - ], - "initialIsOpen": false - }, { "parentPluginId": "presentationUtil", "id": "def-public.ReduxEmbeddablePackage", diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index 0db887f88f4a4..a9ab65763cfb8 100644 --- a/api_docs/presentation_util.mdx +++ b/api_docs/presentation_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationUtil title: "presentationUtil" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationUtil plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationUtil'] --- import presentationUtilObj from './presentation_util.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kib | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 219 | 7 | 163 | 12 | +| 202 | 7 | 146 | 12 | ## Client diff --git a/api_docs/profiling.mdx b/api_docs/profiling.mdx index e0afd607ac132..deba0cdcd5085 100644 --- a/api_docs/profiling.mdx +++ b/api_docs/profiling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profiling title: "profiling" image: https://source.unsplash.com/400x175/?github description: API docs for the profiling plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profiling'] --- import profilingObj from './profiling.devdocs.json'; diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index 8ed90abefbd1a..aaab91fddc3a4 100644 --- a/api_docs/remote_clusters.mdx +++ b/api_docs/remote_clusters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/remoteClusters title: "remoteClusters" image: https://source.unsplash.com/400x175/?github description: API docs for the remoteClusters plugin -date: 2023-02-23 +date: 2023-02-28 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 98e4cb3f4cd38..9dfd0ab61a8ae 100644 --- a/api_docs/reporting.mdx +++ b/api_docs/reporting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/reporting title: "reporting" image: https://source.unsplash.com/400x175/?github description: API docs for the reporting plugin -date: 2023-02-23 +date: 2023-02-28 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 7af3a12c17d3f..a2a19da59470f 100644 --- a/api_docs/rollup.mdx +++ b/api_docs/rollup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/rollup title: "rollup" image: https://source.unsplash.com/400x175/?github description: API docs for the rollup plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'rollup'] --- import rollupObj from './rollup.devdocs.json'; diff --git a/api_docs/rule_registry.devdocs.json b/api_docs/rule_registry.devdocs.json index e9bed596ec7e7..e0588585ba405 100644 --- a/api_docs/rule_registry.devdocs.json +++ b/api_docs/rule_registry.devdocs.json @@ -107,7 +107,7 @@ "label": "get", "description": [], "signature": [ - "({ id, index }: GetAlertParams) => Promise> | undefined>" + "({ id, index }: GetAlertParams) => Promise> | undefined>" ], "path": "x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts", "deprecated": false, @@ -325,7 +325,7 @@ "SortOptions", "[] | undefined; track_total_hits?: boolean | undefined; _source?: string[] | undefined; }) => Promise<", "SearchResponse", - ">, Record>, Record>>" ], @@ -1901,7 +1901,7 @@ "RuleTypeParamsValidator", " | undefined; } | undefined; cancelAlertsOnRuleTimeout?: boolean | undefined; alerts?: ", "IRuleTypeAlerts", - " | undefined; actionGroups: ", + " | undefined; producer: string; actionGroups: ", { "pluginId": "alerting", "scope": "common", @@ -1917,7 +1917,7 @@ "section": "def-common.ActionGroup", "text": "ActionGroup" }, - " | undefined; producer: string; actionVariables?: { context?: ", + " | undefined; actionVariables?: { context?: ", { "pluginId": "alerting", "scope": "common", @@ -2573,7 +2573,7 @@ "signature": [ "> & OutputOf>>>(request: TSearchRequest) => Promise<", + ", TAlertDoc = Partial> & OutputOf>>>(request: TSearchRequest) => Promise<", { "pluginId": "@kbn/es-types", "scope": "common", @@ -3210,7 +3210,7 @@ "label": "getAlertByAlertUuid", "description": [], "signature": [ - "(alertUuid: string) => Promise> & OutputOf>> | null> | null" + "(alertUuid: string) => Promise> & OutputOf>> | null> | null" ], "path": "x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts", "deprecated": false, @@ -4603,7 +4603,7 @@ "label": "parseTechnicalFields", "description": [], "signature": [ - "(input: unknown, partial?: boolean) => OutputOf>" + "(input: unknown, partial?: boolean) => OutputOf>" ], "path": "x-pack/plugins/rule_registry/common/parse_technical_fields.ts", "deprecated": false, @@ -4979,7 +4979,7 @@ "label": "ParsedTechnicalFields", "description": [], "signature": [ - "{ readonly '@timestamp': string; readonly \"kibana.alert.rule.rule_type_id\": string; readonly \"kibana.alert.rule.consumer\": string; readonly \"kibana.alert.rule.producer\": string; readonly \"kibana.space_ids\": string[]; readonly \"kibana.alert.uuid\": string; readonly \"kibana.alert.instance.id\": string; readonly \"kibana.alert.status\": string; readonly \"kibana.alert.rule.category\": string; readonly \"kibana.alert.rule.uuid\": string; readonly \"kibana.alert.rule.name\": string; readonly 'event.action'?: string | undefined; readonly tags?: string[] | undefined; readonly \"kibana.alert.rule.execution.uuid\"?: string | undefined; readonly \"kibana.alert.rule.parameters\"?: { [key: string]: unknown; } | undefined; readonly \"kibana.alert.start\"?: string | undefined; readonly \"kibana.alert.time_range\"?: unknown; readonly \"kibana.alert.end\"?: string | undefined; readonly \"kibana.alert.duration.us\"?: number | undefined; readonly \"kibana.alert.severity\"?: string | undefined; readonly \"kibana.alert.flapping\"?: boolean | undefined; readonly \"kibana.version\"?: string | undefined; readonly \"ecs.version\"?: string | undefined; readonly \"kibana.alert.risk_score\"?: number | undefined; readonly \"kibana.alert.workflow_status\"?: string | undefined; readonly \"kibana.alert.workflow_user\"?: string | undefined; readonly \"kibana.alert.workflow_reason\"?: string | undefined; readonly \"kibana.alert.system_status\"?: string | undefined; readonly \"kibana.alert.action_group\"?: string | undefined; readonly \"kibana.alert.reason\"?: string | undefined; readonly \"kibana.alert.case_ids\"?: string[] | undefined; readonly \"kibana.alert.rule.author\"?: string | undefined; readonly \"kibana.alert.rule.created_at\"?: string | undefined; readonly \"kibana.alert.rule.created_by\"?: string | undefined; readonly \"kibana.alert.rule.description\"?: string | undefined; readonly \"kibana.alert.rule.enabled\"?: string | undefined; readonly \"kibana.alert.rule.from\"?: string | undefined; readonly \"kibana.alert.rule.interval\"?: string | undefined; readonly \"kibana.alert.rule.license\"?: string | undefined; readonly \"kibana.alert.rule.note\"?: string | undefined; readonly \"kibana.alert.rule.references\"?: string[] | undefined; readonly \"kibana.alert.rule.rule_id\"?: string | undefined; readonly \"kibana.alert.rule.rule_name_override\"?: string | undefined; readonly \"kibana.alert.rule.tags\"?: string[] | undefined; readonly \"kibana.alert.rule.to\"?: string | undefined; readonly \"kibana.alert.rule.type\"?: string | undefined; readonly \"kibana.alert.rule.updated_at\"?: string | undefined; readonly \"kibana.alert.rule.updated_by\"?: string | undefined; readonly \"kibana.alert.rule.version\"?: string | undefined; readonly \"kibana.alert.suppression.terms.field\"?: string[] | undefined; readonly \"kibana.alert.suppression.terms.value\"?: string[] | undefined; readonly \"kibana.alert.suppression.start\"?: string | undefined; readonly \"kibana.alert.suppression.end\"?: string | undefined; readonly \"kibana.alert.suppression.docs_count\"?: number | undefined; readonly \"kibana.alert.last_detected\"?: string | undefined; readonly 'event.kind'?: string | undefined; }" + "{ readonly \"@timestamp\": string; readonly \"kibana.alert.rule.rule_type_id\": string; readonly \"kibana.alert.rule.consumer\": string; readonly \"kibana.alert.instance.id\": string; readonly \"kibana.alert.rule.category\": string; readonly \"kibana.alert.rule.name\": string; readonly \"kibana.alert.rule.producer\": string; readonly \"kibana.alert.rule.uuid\": string; readonly \"kibana.alert.status\": string; readonly \"kibana.alert.uuid\": string; readonly \"kibana.space_ids\": string[]; readonly \"event.action\"?: string | undefined; readonly tags?: string[] | undefined; readonly \"kibana.alert.rule.execution.uuid\"?: string | undefined; readonly \"kibana.alert.action_group\"?: string | undefined; readonly \"kibana.alert.case_ids\"?: string[] | undefined; readonly \"kibana.alert.duration.us\"?: number | undefined; readonly \"kibana.alert.end\"?: string | undefined; readonly \"kibana.alert.flapping\"?: boolean | undefined; readonly \"kibana.alert.flapping_history\"?: boolean[] | undefined; readonly \"kibana.alert.last_detected\"?: string | undefined; readonly \"kibana.alert.reason\"?: string | undefined; readonly \"kibana.alert.rule.parameters\"?: { [key: string]: unknown; } | undefined; readonly \"kibana.alert.rule.tags\"?: string[] | undefined; readonly \"kibana.alert.start\"?: string | undefined; readonly \"kibana.alert.time_range\"?: unknown; readonly \"kibana.alert.workflow_status\"?: string | undefined; readonly \"kibana.version\"?: string | undefined; readonly \"kibana.alert.risk_score\"?: number | undefined; readonly \"kibana.alert.rule.author\"?: string | undefined; readonly \"kibana.alert.rule.created_at\"?: string | undefined; readonly \"kibana.alert.rule.created_by\"?: string | undefined; readonly \"kibana.alert.rule.description\"?: string | undefined; readonly \"kibana.alert.rule.enabled\"?: string | undefined; readonly \"kibana.alert.rule.from\"?: string | undefined; readonly \"kibana.alert.rule.interval\"?: string | undefined; readonly \"kibana.alert.rule.license\"?: string | undefined; readonly \"kibana.alert.rule.note\"?: string | undefined; readonly \"kibana.alert.rule.references\"?: string[] | undefined; readonly \"kibana.alert.rule.rule_id\"?: string | undefined; readonly \"kibana.alert.rule.rule_name_override\"?: string | undefined; readonly \"kibana.alert.rule.to\"?: string | undefined; readonly \"kibana.alert.rule.type\"?: string | undefined; readonly \"kibana.alert.rule.updated_at\"?: string | undefined; readonly \"kibana.alert.rule.updated_by\"?: string | undefined; readonly \"kibana.alert.rule.version\"?: string | undefined; readonly \"kibana.alert.severity\"?: string | undefined; readonly \"kibana.alert.suppression.docs_count\"?: number | undefined; readonly \"kibana.alert.suppression.end\"?: string | undefined; readonly \"kibana.alert.suppression.terms.field\"?: string[] | undefined; readonly \"kibana.alert.suppression.start\"?: string | undefined; readonly \"kibana.alert.suppression.terms.value\"?: string[] | undefined; readonly \"kibana.alert.system_status\"?: string | undefined; readonly \"kibana.alert.workflow_reason\"?: string | undefined; readonly \"kibana.alert.workflow_user\"?: string | undefined; readonly \"ecs.version\"?: string | undefined; readonly \"event.kind\"?: string | undefined; }" ], "path": "x-pack/plugins/rule_registry/common/parse_technical_fields.ts", "deprecated": false, diff --git a/api_docs/rule_registry.mdx b/api_docs/rule_registry.mdx index dc0e56aac17c2..bafd718f19619 100644 --- a/api_docs/rule_registry.mdx +++ b/api_docs/rule_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ruleRegistry title: "ruleRegistry" image: https://source.unsplash.com/400x175/?github description: API docs for the ruleRegistry plugin -date: 2023-02-23 +date: 2023-02-28 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 416c7596f1d04..ff7aa047cba3c 100644 --- a/api_docs/runtime_fields.mdx +++ b/api_docs/runtime_fields.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/runtimeFields title: "runtimeFields" image: https://source.unsplash.com/400x175/?github description: API docs for the runtimeFields plugin -date: 2023-02-23 +date: 2023-02-28 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 66bf472079af8..16d7f54784ed1 100644 --- a/api_docs/saved_objects.mdx +++ b/api_docs/saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjects title: "savedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjects plugin -date: 2023-02-23 +date: 2023-02-28 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 3397105d222b4..ace1059c4bed5 100644 --- a/api_docs/saved_objects_finder.mdx +++ b/api_docs/saved_objects_finder.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsFinder title: "savedObjectsFinder" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsFinder plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsFinder'] --- import savedObjectsFinderObj from './saved_objects_finder.devdocs.json'; diff --git a/api_docs/saved_objects_management.devdocs.json b/api_docs/saved_objects_management.devdocs.json index a6df92789fef9..62057eb5bdb1c 100644 --- a/api_docs/saved_objects_management.devdocs.json +++ b/api_docs/saved_objects_management.devdocs.json @@ -308,7 +308,7 @@ "Interpolation", "<", "Theme", - ">; defaultChecked?: boolean | undefined; suppressContentEditableWarning?: boolean | undefined; suppressHydrationWarning?: boolean | undefined; accessKey?: string | undefined; contentEditable?: Booleanish | \"inherit\" | undefined; contextMenu?: string | undefined; dir?: string | undefined; draggable?: Booleanish | undefined; hidden?: boolean | undefined; lang?: string | undefined; placeholder?: string | undefined; spellCheck?: Booleanish | undefined; tabIndex?: number | undefined; translate?: \"yes\" | \"no\" | undefined; radioGroup?: string | undefined; role?: React.AriaRole | undefined; about?: string | undefined; datatype?: string | undefined; inlist?: any; property?: string | undefined; resource?: string | undefined; typeof?: string | undefined; vocab?: string | undefined; autoCapitalize?: string | undefined; autoCorrect?: string | undefined; autoSave?: string | undefined; color?: string | undefined; itemProp?: string | undefined; itemScope?: boolean | undefined; itemType?: string | undefined; itemID?: string | undefined; itemRef?: string | undefined; results?: number | undefined; security?: string | undefined; unselectable?: \"on\" | \"off\" | undefined; inputMode?: \"search\" | \"none\" | \"text\" | \"tel\" | \"url\" | \"email\" | \"numeric\" | \"decimal\" | undefined; 'aria-activedescendant'?: string | undefined; 'aria-atomic'?: Booleanish | undefined; 'aria-autocomplete'?: \"none\" | \"list\" | \"inline\" | \"both\" | undefined; 'aria-busy'?: Booleanish | undefined; 'aria-checked'?: boolean | \"true\" | \"false\" | \"mixed\" | undefined; 'aria-colcount'?: number | undefined; 'aria-colindex'?: number | undefined; 'aria-colspan'?: number | undefined; 'aria-controls'?: string | undefined; 'aria-current'?: boolean | \"page\" | \"date\" | \"time\" | \"true\" | \"false\" | \"step\" | \"location\" | undefined; 'aria-describedby'?: string | undefined; 'aria-details'?: string | undefined; 'aria-disabled'?: Booleanish | undefined; 'aria-dropeffect'?: \"execute\" | \"link\" | \"none\" | \"copy\" | \"move\" | \"popup\" | undefined; 'aria-errormessage'?: string | undefined; 'aria-expanded'?: Booleanish | undefined; 'aria-flowto'?: string | undefined; 'aria-grabbed'?: Booleanish | undefined; 'aria-haspopup'?: boolean | \"dialog\" | \"menu\" | \"true\" | \"false\" | \"grid\" | \"listbox\" | \"tree\" | undefined; 'aria-hidden'?: Booleanish | undefined; 'aria-invalid'?: boolean | \"true\" | \"false\" | \"grammar\" | \"spelling\" | undefined; 'aria-keyshortcuts'?: string | undefined; 'aria-labelledby'?: string | undefined; 'aria-level'?: number | undefined; 'aria-live'?: \"off\" | \"assertive\" | \"polite\" | undefined; 'aria-modal'?: Booleanish | undefined; 'aria-multiline'?: Booleanish | undefined; 'aria-multiselectable'?: Booleanish | undefined; 'aria-orientation'?: \"horizontal\" | \"vertical\" | undefined; 'aria-owns'?: string | undefined; 'aria-placeholder'?: string | undefined; 'aria-posinset'?: number | undefined; 'aria-pressed'?: boolean | \"true\" | \"false\" | \"mixed\" | undefined; 'aria-readonly'?: Booleanish | undefined; 'aria-relevant'?: \"text\" | \"additions\" | \"additions removals\" | \"additions text\" | \"all\" | \"removals\" | \"removals additions\" | \"removals text\" | \"text additions\" | \"text removals\" | undefined; 'aria-required'?: Booleanish | undefined; 'aria-roledescription'?: string | undefined; 'aria-rowcount'?: number | undefined; 'aria-rowindex'?: number | undefined; 'aria-rowspan'?: number | undefined; 'aria-selected'?: Booleanish | undefined; 'aria-setsize'?: number | undefined; 'aria-sort'?: \"none\" | \"ascending\" | \"descending\" | \"other\" | undefined; 'aria-valuemax'?: number | undefined; 'aria-valuemin'?: number | undefined; 'aria-valuenow'?: number | undefined; 'aria-valuetext'?: string | undefined; dangerouslySetInnerHTML?: { __html: string; } | undefined; onCopy?: React.ClipboardEventHandler | undefined; onCopyCapture?: React.ClipboardEventHandler | undefined; onCut?: React.ClipboardEventHandler | undefined; onCutCapture?: React.ClipboardEventHandler | undefined; onPaste?: React.ClipboardEventHandler | undefined; onPasteCapture?: React.ClipboardEventHandler | undefined; onCompositionEnd?: React.CompositionEventHandler | undefined; onCompositionEndCapture?: React.CompositionEventHandler | undefined; onCompositionStart?: React.CompositionEventHandler | undefined; onCompositionStartCapture?: React.CompositionEventHandler | undefined; onCompositionUpdate?: React.CompositionEventHandler | undefined; onCompositionUpdateCapture?: React.CompositionEventHandler | undefined; onFocus?: React.FocusEventHandler | undefined; onFocusCapture?: React.FocusEventHandler | undefined; onBlur?: React.FocusEventHandler | undefined; onBlurCapture?: React.FocusEventHandler | undefined; onChange?: React.FormEventHandler | undefined; onChangeCapture?: React.FormEventHandler | undefined; onBeforeInput?: React.FormEventHandler | undefined; onBeforeInputCapture?: React.FormEventHandler | undefined; onInput?: React.FormEventHandler | undefined; onInputCapture?: React.FormEventHandler | undefined; onReset?: React.FormEventHandler | undefined; onResetCapture?: React.FormEventHandler | undefined; onSubmit?: React.FormEventHandler | undefined; onSubmitCapture?: React.FormEventHandler | undefined; onInvalid?: React.FormEventHandler | undefined; onInvalidCapture?: React.FormEventHandler | undefined; onLoad?: React.ReactEventHandler | undefined; onLoadCapture?: React.ReactEventHandler | undefined; onError?: React.ReactEventHandler | undefined; onErrorCapture?: React.ReactEventHandler | undefined; onKeyDown?: React.KeyboardEventHandler | undefined; onKeyDownCapture?: React.KeyboardEventHandler | undefined; onKeyPress?: React.KeyboardEventHandler | undefined; onKeyPressCapture?: React.KeyboardEventHandler | undefined; onKeyUp?: React.KeyboardEventHandler | undefined; onKeyUpCapture?: React.KeyboardEventHandler | undefined; onAbort?: React.ReactEventHandler | undefined; onAbortCapture?: React.ReactEventHandler | undefined; onCanPlay?: React.ReactEventHandler | undefined; onCanPlayCapture?: React.ReactEventHandler | undefined; onCanPlayThrough?: React.ReactEventHandler | undefined; onCanPlayThroughCapture?: React.ReactEventHandler | undefined; onDurationChange?: React.ReactEventHandler | undefined; onDurationChangeCapture?: React.ReactEventHandler | undefined; onEmptied?: React.ReactEventHandler | undefined; onEmptiedCapture?: React.ReactEventHandler | undefined; onEncrypted?: React.ReactEventHandler | undefined; onEncryptedCapture?: React.ReactEventHandler | undefined; onEnded?: React.ReactEventHandler | undefined; onEndedCapture?: React.ReactEventHandler | undefined; onLoadedData?: React.ReactEventHandler | undefined; onLoadedDataCapture?: React.ReactEventHandler | undefined; onLoadedMetadata?: React.ReactEventHandler | undefined; onLoadedMetadataCapture?: React.ReactEventHandler | undefined; onLoadStart?: React.ReactEventHandler | undefined; onLoadStartCapture?: React.ReactEventHandler | undefined; onPause?: React.ReactEventHandler | undefined; onPauseCapture?: React.ReactEventHandler | undefined; onPlay?: React.ReactEventHandler | undefined; onPlayCapture?: React.ReactEventHandler | undefined; onPlaying?: React.ReactEventHandler | undefined; onPlayingCapture?: React.ReactEventHandler | undefined; onProgress?: React.ReactEventHandler | undefined; onProgressCapture?: React.ReactEventHandler | undefined; onRateChange?: React.ReactEventHandler | undefined; onRateChangeCapture?: React.ReactEventHandler | undefined; onSeeked?: React.ReactEventHandler | undefined; onSeekedCapture?: React.ReactEventHandler | undefined; onSeeking?: React.ReactEventHandler | undefined; onSeekingCapture?: React.ReactEventHandler | undefined; onStalled?: React.ReactEventHandler | undefined; onStalledCapture?: React.ReactEventHandler | undefined; onSuspend?: React.ReactEventHandler | undefined; onSuspendCapture?: React.ReactEventHandler | undefined; onTimeUpdate?: React.ReactEventHandler | undefined; onTimeUpdateCapture?: React.ReactEventHandler | undefined; onVolumeChange?: React.ReactEventHandler | undefined; onVolumeChangeCapture?: React.ReactEventHandler | undefined; onWaiting?: React.ReactEventHandler | undefined; onWaitingCapture?: React.ReactEventHandler | undefined; onAuxClick?: React.MouseEventHandler | undefined; onAuxClickCapture?: React.MouseEventHandler | undefined; onClick?: React.MouseEventHandler | undefined; onClickCapture?: React.MouseEventHandler | undefined; onContextMenu?: React.MouseEventHandler | undefined; onContextMenuCapture?: React.MouseEventHandler | undefined; onDoubleClick?: React.MouseEventHandler | undefined; onDoubleClickCapture?: React.MouseEventHandler | undefined; onDrag?: React.DragEventHandler | undefined; onDragCapture?: React.DragEventHandler | undefined; onDragEnd?: React.DragEventHandler | undefined; onDragEndCapture?: React.DragEventHandler | undefined; onDragEnter?: React.DragEventHandler | undefined; onDragEnterCapture?: React.DragEventHandler | undefined; onDragExit?: React.DragEventHandler | undefined; onDragExitCapture?: React.DragEventHandler | undefined; onDragLeave?: React.DragEventHandler | undefined; onDragLeaveCapture?: React.DragEventHandler | undefined; onDragOver?: React.DragEventHandler | undefined; onDragOverCapture?: React.DragEventHandler | undefined; onDragStart?: React.DragEventHandler | undefined; onDragStartCapture?: React.DragEventHandler | undefined; onDrop?: React.DragEventHandler | undefined; onDropCapture?: React.DragEventHandler | undefined; onMouseDown?: React.MouseEventHandler | undefined; onMouseDownCapture?: React.MouseEventHandler | undefined; onMouseEnter?: React.MouseEventHandler | undefined; onMouseLeave?: React.MouseEventHandler | undefined; onMouseMove?: React.MouseEventHandler | undefined; onMouseMoveCapture?: React.MouseEventHandler | undefined; onMouseOut?: React.MouseEventHandler | undefined; onMouseOutCapture?: React.MouseEventHandler | undefined; onMouseOver?: React.MouseEventHandler | undefined; onMouseOverCapture?: React.MouseEventHandler | undefined; onMouseUp?: React.MouseEventHandler | undefined; onMouseUpCapture?: React.MouseEventHandler | undefined; onSelect?: React.ReactEventHandler | undefined; onSelectCapture?: React.ReactEventHandler | undefined; onTouchCancel?: React.TouchEventHandler | undefined; onTouchCancelCapture?: React.TouchEventHandler | undefined; onTouchEnd?: React.TouchEventHandler | undefined; onTouchEndCapture?: React.TouchEventHandler | undefined; onTouchMove?: React.TouchEventHandler | undefined; onTouchMoveCapture?: React.TouchEventHandler | undefined; onTouchStart?: React.TouchEventHandler | undefined; onTouchStartCapture?: React.TouchEventHandler | undefined; onPointerDown?: React.PointerEventHandler | undefined; onPointerDownCapture?: React.PointerEventHandler | undefined; onPointerMove?: React.PointerEventHandler | undefined; onPointerMoveCapture?: React.PointerEventHandler | undefined; onPointerUp?: React.PointerEventHandler | undefined; onPointerUpCapture?: React.PointerEventHandler | undefined; onPointerCancel?: React.PointerEventHandler | undefined; onPointerCancelCapture?: React.PointerEventHandler | undefined; onPointerEnter?: React.PointerEventHandler | undefined; onPointerEnterCapture?: React.PointerEventHandler | undefined; onPointerLeave?: React.PointerEventHandler | undefined; onPointerLeaveCapture?: React.PointerEventHandler | undefined; onPointerOver?: React.PointerEventHandler | undefined; onPointerOverCapture?: React.PointerEventHandler | undefined; onPointerOut?: React.PointerEventHandler | undefined; onPointerOutCapture?: React.PointerEventHandler | undefined; onGotPointerCapture?: React.PointerEventHandler | undefined; onGotPointerCaptureCapture?: React.PointerEventHandler | undefined; onLostPointerCapture?: React.PointerEventHandler | undefined; onLostPointerCaptureCapture?: React.PointerEventHandler | undefined; onScroll?: React.UIEventHandler | undefined; onScrollCapture?: React.UIEventHandler | undefined; onWheel?: React.WheelEventHandler | undefined; onWheelCapture?: React.WheelEventHandler | undefined; onAnimationStart?: React.AnimationEventHandler | undefined; onAnimationStartCapture?: React.AnimationEventHandler | undefined; onAnimationEnd?: React.AnimationEventHandler | undefined; onAnimationEndCapture?: React.AnimationEventHandler | undefined; onAnimationIteration?: React.AnimationEventHandler | undefined; onAnimationIterationCapture?: React.AnimationEventHandler | undefined; onTransitionEnd?: React.TransitionEventHandler | undefined; onTransitionEndCapture?: React.TransitionEventHandler | undefined; field: (string & {}) | keyof ", + ">; defaultChecked?: boolean | undefined; suppressContentEditableWarning?: boolean | undefined; suppressHydrationWarning?: boolean | undefined; accessKey?: string | undefined; contentEditable?: Booleanish | \"inherit\" | undefined; contextMenu?: string | undefined; dir?: string | undefined; draggable?: Booleanish | undefined; hidden?: boolean | undefined; lang?: string | undefined; placeholder?: string | undefined; spellCheck?: Booleanish | undefined; tabIndex?: number | undefined; translate?: \"yes\" | \"no\" | undefined; radioGroup?: string | undefined; role?: React.AriaRole | undefined; about?: string | undefined; datatype?: string | undefined; inlist?: any; property?: string | undefined; resource?: string | undefined; typeof?: string | undefined; vocab?: string | undefined; autoCapitalize?: string | undefined; autoCorrect?: string | undefined; autoSave?: string | undefined; color?: string | undefined; itemProp?: string | undefined; itemScope?: boolean | undefined; itemType?: string | undefined; itemID?: string | undefined; itemRef?: string | undefined; results?: number | undefined; security?: string | undefined; unselectable?: \"on\" | \"off\" | undefined; inputMode?: \"search\" | \"none\" | \"text\" | \"tel\" | \"url\" | \"email\" | \"numeric\" | \"decimal\" | undefined; 'aria-activedescendant'?: string | undefined; 'aria-atomic'?: Booleanish | undefined; 'aria-autocomplete'?: \"none\" | \"list\" | \"inline\" | \"both\" | undefined; 'aria-busy'?: Booleanish | undefined; 'aria-checked'?: boolean | \"true\" | \"false\" | \"mixed\" | undefined; 'aria-colcount'?: number | undefined; 'aria-colindex'?: number | undefined; 'aria-colspan'?: number | undefined; 'aria-controls'?: string | undefined; 'aria-current'?: boolean | \"page\" | \"date\" | \"time\" | \"true\" | \"false\" | \"step\" | \"location\" | undefined; 'aria-describedby'?: string | undefined; 'aria-details'?: string | undefined; 'aria-disabled'?: Booleanish | undefined; 'aria-dropeffect'?: \"execute\" | \"link\" | \"none\" | \"copy\" | \"move\" | \"popup\" | undefined; 'aria-errormessage'?: string | undefined; 'aria-expanded'?: Booleanish | undefined; 'aria-flowto'?: string | undefined; 'aria-grabbed'?: Booleanish | undefined; 'aria-haspopup'?: boolean | \"dialog\" | \"menu\" | \"true\" | \"false\" | \"grid\" | \"listbox\" | \"tree\" | undefined; 'aria-hidden'?: Booleanish | undefined; 'aria-invalid'?: boolean | \"true\" | \"false\" | \"grammar\" | \"spelling\" | undefined; 'aria-keyshortcuts'?: string | undefined; 'aria-labelledby'?: string | undefined; 'aria-level'?: number | undefined; 'aria-live'?: \"off\" | \"assertive\" | \"polite\" | undefined; 'aria-modal'?: Booleanish | undefined; 'aria-multiline'?: Booleanish | undefined; 'aria-multiselectable'?: Booleanish | undefined; 'aria-orientation'?: \"horizontal\" | \"vertical\" | undefined; 'aria-owns'?: string | undefined; 'aria-placeholder'?: string | undefined; 'aria-posinset'?: number | undefined; 'aria-pressed'?: boolean | \"true\" | \"false\" | \"mixed\" | undefined; 'aria-readonly'?: Booleanish | undefined; 'aria-relevant'?: \"text\" | \"all\" | \"additions\" | \"additions removals\" | \"additions text\" | \"removals\" | \"removals additions\" | \"removals text\" | \"text additions\" | \"text removals\" | undefined; 'aria-required'?: Booleanish | undefined; 'aria-roledescription'?: string | undefined; 'aria-rowcount'?: number | undefined; 'aria-rowindex'?: number | undefined; 'aria-rowspan'?: number | undefined; 'aria-selected'?: Booleanish | undefined; 'aria-setsize'?: number | undefined; 'aria-sort'?: \"none\" | \"ascending\" | \"descending\" | \"other\" | undefined; 'aria-valuemax'?: number | undefined; 'aria-valuemin'?: number | undefined; 'aria-valuenow'?: number | undefined; 'aria-valuetext'?: string | undefined; dangerouslySetInnerHTML?: { __html: string; } | undefined; onCopy?: React.ClipboardEventHandler | undefined; onCopyCapture?: React.ClipboardEventHandler | undefined; onCut?: React.ClipboardEventHandler | undefined; onCutCapture?: React.ClipboardEventHandler | undefined; onPaste?: React.ClipboardEventHandler | undefined; onPasteCapture?: React.ClipboardEventHandler | undefined; onCompositionEnd?: React.CompositionEventHandler | undefined; onCompositionEndCapture?: React.CompositionEventHandler | undefined; onCompositionStart?: React.CompositionEventHandler | undefined; onCompositionStartCapture?: React.CompositionEventHandler | undefined; onCompositionUpdate?: React.CompositionEventHandler | undefined; onCompositionUpdateCapture?: React.CompositionEventHandler | undefined; onFocus?: React.FocusEventHandler | undefined; onFocusCapture?: React.FocusEventHandler | undefined; onBlur?: React.FocusEventHandler | undefined; onBlurCapture?: React.FocusEventHandler | undefined; onChange?: React.FormEventHandler | undefined; onChangeCapture?: React.FormEventHandler | undefined; onBeforeInput?: React.FormEventHandler | undefined; onBeforeInputCapture?: React.FormEventHandler | undefined; onInput?: React.FormEventHandler | undefined; onInputCapture?: React.FormEventHandler | undefined; onReset?: React.FormEventHandler | undefined; onResetCapture?: React.FormEventHandler | undefined; onSubmit?: React.FormEventHandler | undefined; onSubmitCapture?: React.FormEventHandler | undefined; onInvalid?: React.FormEventHandler | undefined; onInvalidCapture?: React.FormEventHandler | undefined; onLoad?: React.ReactEventHandler | undefined; onLoadCapture?: React.ReactEventHandler | undefined; onError?: React.ReactEventHandler | undefined; onErrorCapture?: React.ReactEventHandler | undefined; onKeyDown?: React.KeyboardEventHandler | undefined; onKeyDownCapture?: React.KeyboardEventHandler | undefined; onKeyPress?: React.KeyboardEventHandler | undefined; onKeyPressCapture?: React.KeyboardEventHandler | undefined; onKeyUp?: React.KeyboardEventHandler | undefined; onKeyUpCapture?: React.KeyboardEventHandler | undefined; onAbort?: React.ReactEventHandler | undefined; onAbortCapture?: React.ReactEventHandler | undefined; onCanPlay?: React.ReactEventHandler | undefined; onCanPlayCapture?: React.ReactEventHandler | undefined; onCanPlayThrough?: React.ReactEventHandler | undefined; onCanPlayThroughCapture?: React.ReactEventHandler | undefined; onDurationChange?: React.ReactEventHandler | undefined; onDurationChangeCapture?: React.ReactEventHandler | undefined; onEmptied?: React.ReactEventHandler | undefined; onEmptiedCapture?: React.ReactEventHandler | undefined; onEncrypted?: React.ReactEventHandler | undefined; onEncryptedCapture?: React.ReactEventHandler | undefined; onEnded?: React.ReactEventHandler | undefined; onEndedCapture?: React.ReactEventHandler | undefined; onLoadedData?: React.ReactEventHandler | undefined; onLoadedDataCapture?: React.ReactEventHandler | undefined; onLoadedMetadata?: React.ReactEventHandler | undefined; onLoadedMetadataCapture?: React.ReactEventHandler | undefined; onLoadStart?: React.ReactEventHandler | undefined; onLoadStartCapture?: React.ReactEventHandler | undefined; onPause?: React.ReactEventHandler | undefined; onPauseCapture?: React.ReactEventHandler | undefined; onPlay?: React.ReactEventHandler | undefined; onPlayCapture?: React.ReactEventHandler | undefined; onPlaying?: React.ReactEventHandler | undefined; onPlayingCapture?: React.ReactEventHandler | undefined; onProgress?: React.ReactEventHandler | undefined; onProgressCapture?: React.ReactEventHandler | undefined; onRateChange?: React.ReactEventHandler | undefined; onRateChangeCapture?: React.ReactEventHandler | undefined; onSeeked?: React.ReactEventHandler | undefined; onSeekedCapture?: React.ReactEventHandler | undefined; onSeeking?: React.ReactEventHandler | undefined; onSeekingCapture?: React.ReactEventHandler | undefined; onStalled?: React.ReactEventHandler | undefined; onStalledCapture?: React.ReactEventHandler | undefined; onSuspend?: React.ReactEventHandler | undefined; onSuspendCapture?: React.ReactEventHandler | undefined; onTimeUpdate?: React.ReactEventHandler | undefined; onTimeUpdateCapture?: React.ReactEventHandler | undefined; onVolumeChange?: React.ReactEventHandler | undefined; onVolumeChangeCapture?: React.ReactEventHandler | undefined; onWaiting?: React.ReactEventHandler | undefined; onWaitingCapture?: React.ReactEventHandler | undefined; onAuxClick?: React.MouseEventHandler | undefined; onAuxClickCapture?: React.MouseEventHandler | undefined; onClick?: React.MouseEventHandler | undefined; onClickCapture?: React.MouseEventHandler | undefined; onContextMenu?: React.MouseEventHandler | undefined; onContextMenuCapture?: React.MouseEventHandler | undefined; onDoubleClick?: React.MouseEventHandler | undefined; onDoubleClickCapture?: React.MouseEventHandler | undefined; onDrag?: React.DragEventHandler | undefined; onDragCapture?: React.DragEventHandler | undefined; onDragEnd?: React.DragEventHandler | undefined; onDragEndCapture?: React.DragEventHandler | undefined; onDragEnter?: React.DragEventHandler | undefined; onDragEnterCapture?: React.DragEventHandler | undefined; onDragExit?: React.DragEventHandler | undefined; onDragExitCapture?: React.DragEventHandler | undefined; onDragLeave?: React.DragEventHandler | undefined; onDragLeaveCapture?: React.DragEventHandler | undefined; onDragOver?: React.DragEventHandler | undefined; onDragOverCapture?: React.DragEventHandler | undefined; onDragStart?: React.DragEventHandler | undefined; onDragStartCapture?: React.DragEventHandler | undefined; onDrop?: React.DragEventHandler | undefined; onDropCapture?: React.DragEventHandler | undefined; onMouseDown?: React.MouseEventHandler | undefined; onMouseDownCapture?: React.MouseEventHandler | undefined; onMouseEnter?: React.MouseEventHandler | undefined; onMouseLeave?: React.MouseEventHandler | undefined; onMouseMove?: React.MouseEventHandler | undefined; onMouseMoveCapture?: React.MouseEventHandler | undefined; onMouseOut?: React.MouseEventHandler | undefined; onMouseOutCapture?: React.MouseEventHandler | undefined; onMouseOver?: React.MouseEventHandler | undefined; onMouseOverCapture?: React.MouseEventHandler | undefined; onMouseUp?: React.MouseEventHandler | undefined; onMouseUpCapture?: React.MouseEventHandler | undefined; onSelect?: React.ReactEventHandler | undefined; onSelectCapture?: React.ReactEventHandler | undefined; onTouchCancel?: React.TouchEventHandler | undefined; onTouchCancelCapture?: React.TouchEventHandler | undefined; onTouchEnd?: React.TouchEventHandler | undefined; onTouchEndCapture?: React.TouchEventHandler | undefined; onTouchMove?: React.TouchEventHandler | undefined; onTouchMoveCapture?: React.TouchEventHandler | undefined; onTouchStart?: React.TouchEventHandler | undefined; onTouchStartCapture?: React.TouchEventHandler | undefined; onPointerDown?: React.PointerEventHandler | undefined; onPointerDownCapture?: React.PointerEventHandler | undefined; onPointerMove?: React.PointerEventHandler | undefined; onPointerMoveCapture?: React.PointerEventHandler | undefined; onPointerUp?: React.PointerEventHandler | undefined; onPointerUpCapture?: React.PointerEventHandler | undefined; onPointerCancel?: React.PointerEventHandler | undefined; onPointerCancelCapture?: React.PointerEventHandler | undefined; onPointerEnter?: React.PointerEventHandler | undefined; onPointerEnterCapture?: React.PointerEventHandler | undefined; onPointerLeave?: React.PointerEventHandler | undefined; onPointerLeaveCapture?: React.PointerEventHandler | undefined; onPointerOver?: React.PointerEventHandler | undefined; onPointerOverCapture?: React.PointerEventHandler | undefined; onPointerOut?: React.PointerEventHandler | undefined; onPointerOutCapture?: React.PointerEventHandler | undefined; onGotPointerCapture?: React.PointerEventHandler | undefined; onGotPointerCaptureCapture?: React.PointerEventHandler | undefined; onLostPointerCapture?: React.PointerEventHandler | undefined; onLostPointerCaptureCapture?: React.PointerEventHandler | undefined; onScroll?: React.UIEventHandler | undefined; onScrollCapture?: React.UIEventHandler | undefined; onWheel?: React.WheelEventHandler | undefined; onWheelCapture?: React.WheelEventHandler | undefined; onAnimationStart?: React.AnimationEventHandler | undefined; onAnimationStartCapture?: React.AnimationEventHandler | undefined; onAnimationEnd?: React.AnimationEventHandler | undefined; onAnimationEndCapture?: React.AnimationEventHandler | undefined; onAnimationIteration?: React.AnimationEventHandler | undefined; onAnimationIterationCapture?: React.AnimationEventHandler | undefined; onTransitionEnd?: React.TransitionEventHandler | undefined; onTransitionEndCapture?: React.TransitionEventHandler | undefined; field: (string & {}) | keyof ", { "pluginId": "savedObjectsManagement", "scope": "public", diff --git a/api_docs/saved_objects_management.mdx b/api_docs/saved_objects_management.mdx index a10059e8a28d3..6fd979ecdbb86 100644 --- a/api_docs/saved_objects_management.mdx +++ b/api_docs/saved_objects_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsManagement title: "savedObjectsManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsManagement plugin -date: 2023-02-23 +date: 2023-02-28 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 50a0ae77c7e15..18541c12418b1 100644 --- a/api_docs/saved_objects_tagging.mdx +++ b/api_docs/saved_objects_tagging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTagging title: "savedObjectsTagging" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTagging plugin -date: 2023-02-23 +date: 2023-02-28 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 c30d1d67d3026..442d516371a8d 100644 --- a/api_docs/saved_objects_tagging_oss.mdx +++ b/api_docs/saved_objects_tagging_oss.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTaggingOss title: "savedObjectsTaggingOss" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTaggingOss plugin -date: 2023-02-23 +date: 2023-02-28 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 c75bb8ddcf934..2ddbf2b3f440a 100644 --- a/api_docs/saved_search.mdx +++ b/api_docs/saved_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedSearch title: "savedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the savedSearch plugin -date: 2023-02-23 +date: 2023-02-28 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 385eb3c1adb07..fbab93eac15de 100644 --- a/api_docs/screenshot_mode.mdx +++ b/api_docs/screenshot_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotMode title: "screenshotMode" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotMode plugin -date: 2023-02-23 +date: 2023-02-28 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 fdf978a309f86..5a9f3c7f4a93e 100644 --- a/api_docs/screenshotting.mdx +++ b/api_docs/screenshotting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotting title: "screenshotting" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotting plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; diff --git a/api_docs/security.mdx b/api_docs/security.mdx index 2fbb5c428f3dd..913ea2418a097 100644 --- a/api_docs/security.mdx +++ b/api_docs/security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/security title: "security" image: https://source.unsplash.com/400x175/?github description: API docs for the security plugin -date: 2023-02-23 +date: 2023-02-28 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 037917ccefd0a..f8cb3ecf363dd 100644 --- a/api_docs/security_solution.devdocs.json +++ b/api_docs/security_solution.devdocs.json @@ -95,7 +95,7 @@ "label": "experimentalFeatures", "description": [], "signature": [ - "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly disableIsolationUIPendingStatuses: boolean; readonly pendingActionResponsesWithAck: boolean; readonly policyListEnabled: boolean; readonly policyResponseInFleetEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly responseActionsConsoleEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointRbacEnabled: boolean; readonly endpointRbacV1Enabled: boolean; readonly alertDetailsPageEnabled: boolean; readonly responseActionGetFileEnabled: boolean; readonly responseActionExecuteEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly riskyHostsEnabled: boolean; readonly riskyUsersEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; }" + "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly disableIsolationUIPendingStatuses: boolean; readonly pendingActionResponsesWithAck: boolean; readonly policyListEnabled: boolean; readonly policyResponseInFleetEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly responseActionsConsoleEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointRbacEnabled: boolean; readonly endpointRbacV1Enabled: boolean; readonly alertDetailsPageEnabled: boolean; readonly responseActionGetFileEnabled: boolean; readonly responseActionExecuteEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly securityFlyoutEnabled: boolean; readonly riskyHostsEnabled: boolean; readonly riskyUsersEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; }" ], "path": "x-pack/plugins/security_solution/public/plugin.tsx", "deprecated": false, diff --git a/api_docs/security_solution.mdx b/api_docs/security_solution.mdx index cfb431fd9c9a6..eeced72ec06fe 100644 --- a/api_docs/security_solution.mdx +++ b/api_docs/security_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolution title: "securitySolution" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolution plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx index 03c4b57600fed..597c0533242ca 100644 --- a/api_docs/session_view.mdx +++ b/api_docs/session_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/sessionView title: "sessionView" image: https://source.unsplash.com/400x175/?github description: API docs for the sessionView plugin -date: 2023-02-23 +date: 2023-02-28 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 bb7390ac6f6e5..ce9b271f3639c 100644 --- a/api_docs/share.mdx +++ b/api_docs/share.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/share title: "share" image: https://source.unsplash.com/400x175/?github description: API docs for the share plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'share'] --- import shareObj from './share.devdocs.json'; diff --git a/api_docs/snapshot_restore.mdx b/api_docs/snapshot_restore.mdx index 7274ecfc7808d..5ffe5a5072c6f 100644 --- a/api_docs/snapshot_restore.mdx +++ b/api_docs/snapshot_restore.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/snapshotRestore title: "snapshotRestore" image: https://source.unsplash.com/400x175/?github description: API docs for the snapshotRestore plugin -date: 2023-02-23 +date: 2023-02-28 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 89bb472fe6015..f05bf8794e108 100644 --- a/api_docs/spaces.mdx +++ b/api_docs/spaces.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/spaces title: "spaces" image: https://source.unsplash.com/400x175/?github description: API docs for the spaces plugin -date: 2023-02-23 +date: 2023-02-28 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 d0d165c22f28f..53063d23c2cc2 100644 --- a/api_docs/stack_alerts.mdx +++ b/api_docs/stack_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackAlerts title: "stackAlerts" image: https://source.unsplash.com/400x175/?github description: API docs for the stackAlerts plugin -date: 2023-02-23 +date: 2023-02-28 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 595ad4f14c7c3..7f1f52fa198db 100644 --- a/api_docs/stack_connectors.mdx +++ b/api_docs/stack_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackConnectors title: "stackConnectors" image: https://source.unsplash.com/400x175/?github description: API docs for the stackConnectors plugin -date: 2023-02-23 +date: 2023-02-28 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 da0222ea7d4c3..aa72caf8ee862 100644 --- a/api_docs/task_manager.mdx +++ b/api_docs/task_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/taskManager title: "taskManager" image: https://source.unsplash.com/400x175/?github description: API docs for the taskManager plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'taskManager'] --- import taskManagerObj from './task_manager.devdocs.json'; diff --git a/api_docs/telemetry.devdocs.json b/api_docs/telemetry.devdocs.json index ce31c34065ea5..cca1ac23391c3 100644 --- a/api_docs/telemetry.devdocs.json +++ b/api_docs/telemetry.devdocs.json @@ -119,7 +119,7 @@ "Should the telemetry payloads be sent from the server or the browser?" ], "signature": [ - "\"server\" | \"browser\"" + "\"browser\" | \"server\"" ], "path": "src/plugins/telemetry/public/plugin.ts", "deprecated": false, diff --git a/api_docs/telemetry.mdx b/api_docs/telemetry.mdx index a04f65af1aaf1..ead6ab9d2fc06 100644 --- a/api_docs/telemetry.mdx +++ b/api_docs/telemetry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetry title: "telemetry" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetry plugin -date: 2023-02-23 +date: 2023-02-28 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 02eaac3a4326a..bea30d56e7f3a 100644 --- a/api_docs/telemetry_collection_manager.mdx +++ b/api_docs/telemetry_collection_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionManager title: "telemetryCollectionManager" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionManager plugin -date: 2023-02-23 +date: 2023-02-28 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 0f8976108f6af..2eaa03df603b3 100644 --- a/api_docs/telemetry_collection_xpack.mdx +++ b/api_docs/telemetry_collection_xpack.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionXpack title: "telemetryCollectionXpack" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionXpack plugin -date: 2023-02-23 +date: 2023-02-28 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 db9c8ee366273..e445e28aa62ef 100644 --- a/api_docs/telemetry_management_section.mdx +++ b/api_docs/telemetry_management_section.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryManagementSection title: "telemetryManagementSection" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryManagementSection plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryManagementSection'] --- import telemetryManagementSectionObj from './telemetry_management_section.devdocs.json'; diff --git a/api_docs/threat_intelligence.mdx b/api_docs/threat_intelligence.mdx index 566b102c036eb..14fe41c6b8535 100644 --- a/api_docs/threat_intelligence.mdx +++ b/api_docs/threat_intelligence.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/threatIntelligence title: "threatIntelligence" image: https://source.unsplash.com/400x175/?github description: API docs for the threatIntelligence plugin -date: 2023-02-23 +date: 2023-02-28 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 48eaa3ba1a761..34740ef1b0b8a 100644 --- a/api_docs/timelines.mdx +++ b/api_docs/timelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/timelines title: "timelines" image: https://source.unsplash.com/400x175/?github description: API docs for the timelines plugin -date: 2023-02-23 +date: 2023-02-28 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 2a865238676dd..860c46a6aaca7 100644 --- a/api_docs/transform.mdx +++ b/api_docs/transform.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/transform title: "transform" image: https://source.unsplash.com/400x175/?github description: API docs for the transform plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'transform'] --- import transformObj from './transform.devdocs.json'; diff --git a/api_docs/triggers_actions_ui.devdocs.json b/api_docs/triggers_actions_ui.devdocs.json index acac8c2932fb4..a5736cefd3749 100644 --- a/api_docs/triggers_actions_ui.devdocs.json +++ b/api_docs/triggers_actions_ui.devdocs.json @@ -2703,7 +2703,7 @@ "description": [], "signature": [ "BasicFields", - " & { \"@timestamp\"?: string[] | undefined; \"event.action\"?: string[] | undefined; tags?: string[] | undefined; kibana?: string[] | undefined; \"kibana.alert.rule.rule_type_id\"?: string[] | undefined; \"kibana.alert.rule.consumer\"?: string[] | undefined; \"kibana.alert.rule.execution.uuid\"?: string[] | undefined; \"kibana.alert\"?: string[] | undefined; \"kibana.alert.rule\"?: string[] | undefined; \"kibana.alert.rule.parameters\"?: string[] | undefined; \"kibana.alert.rule.producer\"?: string[] | undefined; \"kibana.space_ids\"?: string[] | undefined; \"kibana.alert.uuid\"?: string[] | undefined; \"kibana.alert.instance.id\"?: string[] | undefined; \"kibana.alert.start\"?: string[] | undefined; \"kibana.alert.time_range\"?: string[] | undefined; \"kibana.alert.end\"?: string[] | undefined; \"kibana.alert.duration.us\"?: string[] | undefined; \"kibana.alert.severity\"?: string[] | undefined; \"kibana.alert.status\"?: string[] | undefined; \"kibana.alert.flapping\"?: string[] | undefined; \"kibana.version\"?: string[] | undefined; \"ecs.version\"?: string[] | undefined; \"kibana.alert.risk_score\"?: string[] | undefined; \"kibana.alert.workflow_status\"?: string[] | undefined; \"kibana.alert.workflow_user\"?: string[] | undefined; \"kibana.alert.workflow_reason\"?: string[] | undefined; \"kibana.alert.system_status\"?: string[] | undefined; \"kibana.alert.action_group\"?: string[] | undefined; \"kibana.alert.reason\"?: string[] | undefined; \"kibana.alert.case_ids\"?: string[] | undefined; \"kibana.alert.rule.author\"?: string[] | undefined; \"kibana.alert.rule.category\"?: string[] | undefined; \"kibana.alert.rule.uuid\"?: string[] | undefined; \"kibana.alert.rule.created_at\"?: string[] | undefined; \"kibana.alert.rule.created_by\"?: string[] | undefined; \"kibana.alert.rule.description\"?: string[] | undefined; \"kibana.alert.rule.enabled\"?: string[] | undefined; \"kibana.alert.rule.from\"?: string[] | undefined; \"kibana.alert.rule.interval\"?: string[] | undefined; \"kibana.alert.rule.license\"?: string[] | undefined; \"kibana.alert.rule.name\"?: string[] | undefined; \"kibana.alert.rule.note\"?: string[] | undefined; \"kibana.alert.rule.references\"?: string[] | undefined; \"kibana.alert.rule.rule_id\"?: string[] | undefined; \"kibana.alert.rule.rule_name_override\"?: string[] | undefined; \"kibana.alert.rule.tags\"?: string[] | undefined; \"kibana.alert.rule.to\"?: string[] | undefined; \"kibana.alert.rule.type\"?: string[] | undefined; \"kibana.alert.rule.updated_at\"?: string[] | undefined; \"kibana.alert.rule.updated_by\"?: string[] | undefined; \"kibana.alert.rule.version\"?: string[] | undefined; \"kibana.alert.suppression.terms\"?: string[] | undefined; \"kibana.alert.suppression.terms.field\"?: string[] | undefined; \"kibana.alert.suppression.terms.value\"?: string[] | undefined; \"kibana.alert.suppression.start\"?: string[] | undefined; \"kibana.alert.suppression.end\"?: string[] | undefined; \"kibana.alert.suppression.docs_count\"?: string[] | undefined; \"event.kind\"?: string[] | undefined; \"event.module\"?: string[] | undefined; \"kibana.alert.evaluation.threshold\"?: string[] | undefined; \"kibana.alert.evaluation.value\"?: string[] | undefined; \"kibana.alert.building_block_type\"?: string[] | undefined; \"kibana.alert.rule.exceptions_list\"?: string[] | undefined; \"kibana.alert.rule.namespace\"?: string[] | undefined; \"kibana.alert.rule.threat.framework\"?: string[] | undefined; \"kibana.alert.rule.threat.tactic.id\"?: string[] | undefined; \"kibana.alert.rule.threat.tactic.name\"?: string[] | undefined; \"kibana.alert.rule.threat.tactic.reference\"?: string[] | undefined; \"kibana.alert.rule.threat.technique.id\"?: string[] | undefined; \"kibana.alert.rule.threat.technique.name\"?: string[] | undefined; \"kibana.alert.rule.threat.technique.reference\"?: string[] | undefined; \"kibana.alert.rule.threat.technique.subtechnique.id\"?: string[] | undefined; \"kibana.alert.rule.threat.technique.subtechnique.name\"?: string[] | undefined; \"kibana.alert.rule.threat.technique.subtechnique.reference\"?: string[] | undefined; } & { [x: string]: unknown[]; }" + " & { \"@timestamp\"?: string[] | undefined; \"event.action\"?: string[] | undefined; tags?: string[] | undefined; kibana?: string[] | undefined; \"kibana.alert.rule.rule_type_id\"?: string[] | undefined; \"kibana.alert.rule.consumer\"?: string[] | undefined; \"kibana.alert.rule.execution.uuid\"?: string[] | undefined; \"kibana.alert\"?: string[] | undefined; \"kibana.alert.action_group\"?: string[] | undefined; \"kibana.alert.case_ids\"?: string[] | undefined; \"kibana.alert.duration.us\"?: string[] | undefined; \"kibana.alert.end\"?: string[] | undefined; \"kibana.alert.flapping\"?: string[] | undefined; \"kibana.alert.instance.id\"?: string[] | undefined; \"kibana.alert.reason\"?: string[] | undefined; \"kibana.alert.rule\"?: string[] | undefined; \"kibana.alert.rule.category\"?: string[] | undefined; \"kibana.alert.rule.name\"?: string[] | undefined; \"kibana.alert.rule.parameters\"?: string[] | undefined; \"kibana.alert.rule.producer\"?: string[] | undefined; \"kibana.alert.rule.tags\"?: string[] | undefined; \"kibana.alert.rule.uuid\"?: string[] | undefined; \"kibana.alert.start\"?: string[] | undefined; \"kibana.alert.status\"?: string[] | undefined; \"kibana.alert.time_range\"?: string[] | undefined; \"kibana.alert.uuid\"?: string[] | undefined; \"kibana.alert.workflow_status\"?: string[] | undefined; \"kibana.space_ids\"?: string[] | undefined; \"kibana.version\"?: string[] | undefined; \"kibana.alert.risk_score\"?: string[] | undefined; \"kibana.alert.rule.author\"?: string[] | undefined; \"kibana.alert.rule.created_at\"?: string[] | undefined; \"kibana.alert.rule.created_by\"?: string[] | undefined; \"kibana.alert.rule.description\"?: string[] | undefined; \"kibana.alert.rule.enabled\"?: string[] | undefined; \"kibana.alert.rule.from\"?: string[] | undefined; \"kibana.alert.rule.interval\"?: string[] | undefined; \"kibana.alert.rule.license\"?: string[] | undefined; \"kibana.alert.rule.note\"?: string[] | undefined; \"kibana.alert.rule.references\"?: string[] | undefined; \"kibana.alert.rule.rule_id\"?: string[] | undefined; \"kibana.alert.rule.rule_name_override\"?: string[] | undefined; \"kibana.alert.rule.to\"?: string[] | undefined; \"kibana.alert.rule.type\"?: string[] | undefined; \"kibana.alert.rule.updated_at\"?: string[] | undefined; \"kibana.alert.rule.updated_by\"?: string[] | undefined; \"kibana.alert.rule.version\"?: string[] | undefined; \"kibana.alert.severity\"?: string[] | undefined; \"kibana.alert.suppression.docs_count\"?: string[] | undefined; \"kibana.alert.suppression.end\"?: string[] | undefined; \"kibana.alert.suppression.terms\"?: string[] | undefined; \"kibana.alert.suppression.terms.field\"?: string[] | undefined; \"kibana.alert.suppression.start\"?: string[] | undefined; \"kibana.alert.suppression.terms.value\"?: string[] | undefined; \"kibana.alert.system_status\"?: string[] | undefined; \"kibana.alert.workflow_reason\"?: string[] | undefined; \"kibana.alert.workflow_user\"?: string[] | undefined; \"ecs.version\"?: string[] | undefined; \"event.kind\"?: string[] | undefined; \"kibana.alert.evaluation.threshold\"?: string[] | undefined; \"kibana.alert.evaluation.value\"?: string[] | undefined; \"event.module\"?: string[] | undefined; \"kibana.alert.building_block_type\"?: string[] | undefined; \"kibana.alert.rule.exceptions_list\"?: string[] | undefined; \"kibana.alert.rule.namespace\"?: string[] | undefined; \"kibana.alert.rule.threat.framework\"?: string[] | undefined; \"kibana.alert.rule.threat.tactic.id\"?: string[] | undefined; \"kibana.alert.rule.threat.tactic.name\"?: string[] | undefined; \"kibana.alert.rule.threat.tactic.reference\"?: string[] | undefined; \"kibana.alert.rule.threat.technique.id\"?: string[] | undefined; \"kibana.alert.rule.threat.technique.name\"?: string[] | undefined; \"kibana.alert.rule.threat.technique.reference\"?: string[] | undefined; \"kibana.alert.rule.threat.technique.subtechnique.id\"?: string[] | undefined; \"kibana.alert.rule.threat.technique.subtechnique.name\"?: string[] | undefined; \"kibana.alert.rule.threat.technique.subtechnique.reference\"?: string[] | undefined; } & { [x: string]: unknown[]; }" ], "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", "deprecated": false, @@ -4172,7 +4172,7 @@ "section": "def-common.RuleType", "text": "RuleType" }, - ", \"id\" | \"name\" | \"actionGroups\" | \"defaultActionGroupId\" | \"recoveryActionGroup\" | \"producer\" | \"minimumLicenseRequired\" | \"defaultScheduleInterval\" | \"ruleTaskTimeout\" | \"doesSetRecoveryContext\">" + ", \"id\" | \"name\" | \"producer\" | \"actionGroups\" | \"defaultActionGroupId\" | \"recoveryActionGroup\" | \"minimumLicenseRequired\" | \"defaultScheduleInterval\" | \"ruleTaskTimeout\" | \"doesSetRecoveryContext\">" ], "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", "deprecated": false, diff --git a/api_docs/triggers_actions_ui.mdx b/api_docs/triggers_actions_ui.mdx index 1ed9066cbc8f2..2aab69671f429 100644 --- a/api_docs/triggers_actions_ui.mdx +++ b/api_docs/triggers_actions_ui.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/triggersActionsUi title: "triggersActionsUi" image: https://source.unsplash.com/400x175/?github description: API docs for the triggersActionsUi plugin -date: 2023-02-23 +date: 2023-02-28 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 8393397b07c8c..c8bbb58cd83b2 100644 --- a/api_docs/ui_actions.mdx +++ b/api_docs/ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActions title: "uiActions" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActions plugin -date: 2023-02-23 +date: 2023-02-28 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 6381a8fa801a1..7aa3001fe08eb 100644 --- a/api_docs/ui_actions_enhanced.mdx +++ b/api_docs/ui_actions_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActionsEnhanced title: "uiActionsEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActionsEnhanced plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.devdocs.json'; diff --git a/api_docs/unified_field_list.mdx b/api_docs/unified_field_list.mdx index d500c1e63643e..6fe0b991d6fa1 100644 --- a/api_docs/unified_field_list.mdx +++ b/api_docs/unified_field_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedFieldList title: "unifiedFieldList" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedFieldList plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedFieldList'] --- import unifiedFieldListObj from './unified_field_list.devdocs.json'; diff --git a/api_docs/unified_histogram.mdx b/api_docs/unified_histogram.mdx index 423b6f1757418..11a20aaa28491 100644 --- a/api_docs/unified_histogram.mdx +++ b/api_docs/unified_histogram.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedHistogram title: "unifiedHistogram" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedHistogram plugin -date: 2023-02-23 +date: 2023-02-28 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 bd52a80e6090f..64d361f9de3a8 100644 --- a/api_docs/unified_search.mdx +++ b/api_docs/unified_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch title: "unifiedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch plugin -date: 2023-02-23 +date: 2023-02-28 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 1aee59a78faf5..938acbf44f8f1 100644 --- a/api_docs/unified_search_autocomplete.mdx +++ b/api_docs/unified_search_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch-autocomplete title: "unifiedSearch.autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch.autocomplete plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete'] --- import unifiedSearchAutocompleteObj from './unified_search_autocomplete.devdocs.json'; diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index ffc0f7231a8de..be3bbff9a466a 100644 --- a/api_docs/url_forwarding.mdx +++ b/api_docs/url_forwarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/urlForwarding title: "urlForwarding" image: https://source.unsplash.com/400x175/?github description: API docs for the urlForwarding plugin -date: 2023-02-23 +date: 2023-02-28 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 84090c009a041..36998816dcddc 100644 --- a/api_docs/usage_collection.mdx +++ b/api_docs/usage_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/usageCollection title: "usageCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the usageCollection plugin -date: 2023-02-23 +date: 2023-02-28 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 7fe947d440fd2..185f5306ae783 100644 --- a/api_docs/ux.mdx +++ b/api_docs/ux.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ux title: "ux" image: https://source.unsplash.com/400x175/?github description: API docs for the ux plugin -date: 2023-02-23 +date: 2023-02-28 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 eddb2a97ca938..2bb4750747ba0 100644 --- a/api_docs/vis_default_editor.mdx +++ b/api_docs/vis_default_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visDefaultEditor title: "visDefaultEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the visDefaultEditor plugin -date: 2023-02-23 +date: 2023-02-28 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 e0fe4654b4cdd..8d9aea5b9f72e 100644 --- a/api_docs/vis_type_gauge.mdx +++ b/api_docs/vis_type_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeGauge title: "visTypeGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeGauge plugin -date: 2023-02-23 +date: 2023-02-28 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 a95a480c2f8de..efe160318e302 100644 --- a/api_docs/vis_type_heatmap.mdx +++ b/api_docs/vis_type_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeHeatmap title: "visTypeHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeHeatmap plugin -date: 2023-02-23 +date: 2023-02-28 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 cf6d0f7ba482b..42329bc9873f6 100644 --- a/api_docs/vis_type_pie.mdx +++ b/api_docs/vis_type_pie.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypePie title: "visTypePie" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypePie plugin -date: 2023-02-23 +date: 2023-02-28 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 38962b57cb3a7..a4d39e8071188 100644 --- a/api_docs/vis_type_table.mdx +++ b/api_docs/vis_type_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTable title: "visTypeTable" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTable plugin -date: 2023-02-23 +date: 2023-02-28 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 36433f90c0cec..c7663b05e89cc 100644 --- a/api_docs/vis_type_timelion.mdx +++ b/api_docs/vis_type_timelion.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimelion title: "visTypeTimelion" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimelion plugin -date: 2023-02-23 +date: 2023-02-28 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 5ab85f63e1adc..62b8b9db10bc0 100644 --- a/api_docs/vis_type_timeseries.mdx +++ b/api_docs/vis_type_timeseries.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimeseries title: "visTypeTimeseries" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimeseries plugin -date: 2023-02-23 +date: 2023-02-28 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 9e37f240a182e..2cccc99f502f2 100644 --- a/api_docs/vis_type_vega.mdx +++ b/api_docs/vis_type_vega.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVega title: "visTypeVega" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVega plugin -date: 2023-02-23 +date: 2023-02-28 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 a38e41d152324..813c2d581da8f 100644 --- a/api_docs/vis_type_vislib.mdx +++ b/api_docs/vis_type_vislib.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVislib title: "visTypeVislib" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVislib plugin -date: 2023-02-23 +date: 2023-02-28 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 b9d08df27589b..7530cbec77b3f 100644 --- a/api_docs/vis_type_xy.mdx +++ b/api_docs/vis_type_xy.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeXy title: "visTypeXy" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeXy plugin -date: 2023-02-23 +date: 2023-02-28 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 799d254bbb9cc..638333102c3b5 100644 --- a/api_docs/visualizations.mdx +++ b/api_docs/visualizations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visualizations title: "visualizations" image: https://source.unsplash.com/400x175/?github description: API docs for the visualizations plugin -date: 2023-02-23 +date: 2023-02-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; diff --git a/docs/rule-type-template.asciidoc b/docs/rule-type-template.asciidoc index 5fe4de81bcddc..5d36f1bbccf95 100644 --- a/docs/rule-type-template.asciidoc +++ b/docs/rule-type-template.asciidoc @@ -6,7 +6,7 @@ Include a short description of the rule type. [float] ==== Create the rule -Fill in the <>, then select **. +Fill in the name and optional tags, then select **. [float] ==== Define the conditions diff --git a/docs/user/alerting/action-variables.asciidoc b/docs/user/alerting/action-variables.asciidoc new file mode 100644 index 0000000000000..24f4da2320995 --- /dev/null +++ b/docs/user/alerting/action-variables.asciidoc @@ -0,0 +1,126 @@ +[[rule-action-variables]] +== Rule action variables + +Alerting rules can use the https://mustache.github.io/[Mustache] template syntax +(`{{variable name}}`) to pass values when its actions run. + +The available variables differ by rule type, however there are some common variables: + +* <> +* <> +* <> + +Some cases exist where the variable values will be "escaped" when used in a context where escaping is needed. For example: + +- For the <>, the `message` action configuration property escapes any characters that would be interpreted as Markdown. +- For the <>, the `message` action configuration property escapes any characters that would be interpreted as Slack Markdown. +- For the <>, the `body` action configuration property escapes any characters that are invalid in JSON string values. + +Mustache also supports "triple braces" of the form `{{{variable name}}}`, which indicates no escaping should be done at all. Use this form with caution, since it could end up rendering the variable content such that the resulting parameter is invalid or formatted incorrectly. + +[float] +[[general-rule-action-variables]] +=== General + +All rule types pass the following variables: + +`date`:: The date the rule scheduled the action, in ISO format. +`kibanaBaseUrl`:: The configured <>. If not configured, this will be empty. +`rule.id`:: The ID of the rule. +`rule.name`:: The name of the rule. +`rule.spaceId`:: The ID of the space for the rule. +`rule.tags`:: The list of tags applied to the rule. + +[float] +[role="child_attributes"] +[[alert-summary-action-variables]] +=== Action frequency: Summary of alerts + +If the rule's action frequency is a summary of alerts, it passes the following variables: + +`alerts.all.count`:: The count of all alerts. + +`alerts.all.data`:: +An array of objects for all alerts. The following object properties are examples; it is not a comprehensive list. ++ +.Properties of the alerts.all.data objects +[%collapsible%open] +===== +//# tag::alerts-data[] +`kibana.alert.end`:: Datetime stamp of alert end. preview:[] +`kibana.alert.flapping`:: A flag on the alert that indicates whether the alert status is changing repeatedly. preview:[] +`kibana.alert.instance.id`:: ID of the source that generates the alert. preview:[] +`kibana.alert.reason`:: The reason of the alert (generated with the rule conditions). preview:[] +`kibana.alert.start`:: Datetime stamp of alert start. preview:[] +`kibana.alert.status`:: Alert status (for example, active or OK). preview:[] +//# end::alerts-data[] +===== + +`alerts.new.count`:: The count of new alerts. + +`alerts.new.data`:: +An array of objects for new alerts. The following object properties are examples; it is not a comprehensive list. ++ +.Properties of the alerts.new.data objects +[%collapsible] +===== +include::action-variables.asciidoc[tag=alerts-data] +===== + +`alerts.ongoing.count`:: The count of ongoing alerts. + +`alerts.ongoing.data`:: +An array of objects for ongoing alerts. The following object properties are examples; it is not a comprehensive list. ++ +.Properties of the alerts.ongoing.data objects +[%collapsible] +===== +include::action-variables.asciidoc[tag=alerts-data] +===== + +`alerts.recovered.count`:: The count of recovered alerts. + +`alerts.recovered.data`:: +An array of objects for recovered alerts. The following object properties are examples; it is not a comprehensive list. ++ +.Properties of the alerts.recovered.data objects +[%collapsible] +===== +include::action-variables.asciidoc[tag=alerts-data] +===== + +[float] +[[alert-action-variables]] +=== Action frequency: For each alert + +If the rule's action frequency is not a summary of alerts, it passes the following variables: + +`alert.actionGroup`:: The ID of the action group of the alert that scheduled the action. +`alert.actionGroupName`:: The name of the action group of the alert that scheduled the action. +`alert.actionSubgroup`:: The action subgroup of the alert that scheduled the action. +`alert.flapping`:: A flag on the alert that indicates whether the alert status is changing repeatedly. +`alert.id`:: The ID of the alert that scheduled the action. + +[float] +[[defining-rules-actions-variable-context]] +==== Context + +If the rule's action frequency is not a summary of alerts, the rule defines additional variables as properties of the variable `context`. For example, if a rule type defines a variable `value`, it can be used in an action parameter as `{{context.value}}`. + +For diagnostic or exploratory purposes, action variables whose values are objects, such as `context`, can be referenced directly as variables. The resulting value will be a JSON representation of the object. For example, if an action parameter includes `{{context}}`, it will expand to the JSON representation of all the variables and values provided by the rule type. To see alert-specific variables, use `{{.}}`. + +For situations where your rule response returns arrays of data, you can loop through the `context`: + +[source] +-------------------------------------------------- +{{#context}}{{.}}{{/context}} +-------------------------------------------------- + +For example, looping through search result hits: + +[source] +-------------------------------------------------- +triggering data was: +{{#context.hits}} - {{_source.message}} +{{/context.hits}} +-------------------------------------------------- diff --git a/docs/user/alerting/alerting-getting-started.asciidoc b/docs/user/alerting/alerting-getting-started.asciidoc index e169bcfc24869..b7ca2f3c58b55 100644 --- a/docs/user/alerting/alerting-getting-started.asciidoc +++ b/docs/user/alerting/alerting-getting-started.asciidoc @@ -3,7 +3,7 @@ -- -Alerting allows you to define _rules_ to detect complex conditions within different {kib} apps and trigger actions when those conditions are met. Alerting is integrated with {observability-guide}/create-alerts.html[*Observability*], {security-guide}/prebuilt-rules.html[*Security*], <> and {ml-docs}/ml-configuring-alerts.html[*{ml-app}*], can be centrally managed from the <> UI, and provides a set of built-in <> and <> (known as stack rules) for you to use. +Alerting enables you to define _rules_, which detect complex conditions within different {kib} apps and trigger actions when those conditions are met. Alerting is integrated with {observability-guide}/create-alerts.html[*{observability}*], {security-guide}/prebuilt-rules.html[*Security*], <> and {ml-docs}/ml-configuring-alerts.html[*{ml-app}*]. It can be centrally managed from *{stack-manage-app}* and provides a set of built-in <> and <> for you to use. image::images/alerting-overview.png[{rules-ui} UI] @@ -12,15 +12,12 @@ image::images/alerting-overview.png[{rules-ui} UI] To make sure you can access alerting and actions, see the <> section. ============================================== -[float] -== Concepts and terminology - Alerting works by running checks on a schedule to detect conditions defined by a rule. When a condition is met, the rule tracks it as an _alert_ and responds by triggering one or more _actions_. -Actions typically involve interaction with {kib} services or third party integrations. _Connectors_ allow actions to talk to these services and integrations. +Actions typically involve interaction with {kib} services or third party integrations. _Connectors_ enable actions to talk to these services and integrations. This section describes all of these elements and how they operate together. [float] -=== Rules +== Rules A rule specifies a background task that runs on the {kib} server to check for specific conditions. {kib} provides two types of rules: stack rules that are built into {kib} and the rules that are registered by {kib} apps. For more information, refer to <>. @@ -42,7 +39,7 @@ The following sections describe each part of the rule in more detail. [float] [[alerting-concepts-conditions]] -==== Conditions +=== Conditions Under the hood, {kib} rules detect conditions by running a JavaScript function on the {kib} server, which gives it the flexibility to support a wide range of conditions, anything from the results of a simple {es} query to heavy computations involving data from multiple sources or external systems. @@ -55,58 +52,47 @@ See <> for the rules provided by {kib} and how they express their co [float] [[alerting-concepts-scheduling]] -==== Schedule +=== Schedule Rule schedules are defined as an interval between subsequent checks, and can range from a few seconds to months. [IMPORTANT] ============================================== -The intervals of rule checks in {kib} are approximate. Their timing is affected by factors such as the frequency at which tasks are claimed and the task load on the system. Refer to <> for more information. +The intervals of rule checks in {kib} are approximate. Their timing is affected by factors such as the frequency at which tasks are claimed and the task load on the system. Refer to <> for more information. ============================================== [float] [[alerting-concepts-actions]] -==== Actions +=== Actions -Actions are invocations of connectors, which allow interaction with {kib} services or integrations with third-party systems. Actions run as background tasks on the {kib} server when rule conditions are met. +Actions run as background tasks on the {kib} server when rule conditions are met. Recovery actions likewise run when rule conditions are no longer met. They send notifications by connecting with services inside {kib} or integrating with third-party systems. When defining actions in a rule, you specify: -* The _connector type_: the type of service or integration to use -* The connection for that type by referencing a <> +* A connector +* An action frequency * A mapping of rule values to properties exposed for that type of action -The result is a template: all the parameters needed to invoke a service are supplied except for specific values that are only known at the time the rule condition is detected. +Rather than repeatedly entering connection information and credentials for each action, {kib} simplifies action setup using <>. For example if four rules send email notifications via the same SMTP service, they can all reference the same SMTP connector. -In the server monitoring example, the `email` connector type is used, and `server` is mapped to the body of the email, using the template string `CPU on {{server}} is high`. +The _action frequency_ defines when the action runs (for example, only when the alert status changes or at specific time intervals). Each rule type also has a set of the _action groups_ that affects when the action runs (for example, when the threshold is met or when the alert is recovered). If you want to reduce the number of notifications you receive without affecting their timeliness, some rule types support alert summaries. You can set the action frequency such that you receive notifications that summarize the new, ongoing, and recovered alerts at your preferred time intervals. -When the rule detects the condition, it creates an <> containing the details of the condition, renders the template with these details such as server name, and runs the action on the {kib} server by invoking the `email` connector type. +Each action definition is therefore a template: all the parameters needed to invoke a service are supplied except for specific values that are only known at the time the rule condition is detected. -image::images/what-is-an-action.svg[Actions are like templates that are rendered when an alert detects a condition] +In the server monitoring example, the `email` connector type is used, and `server` is mapped to the body of the email, using the template string `CPU on {{server}} is high`. -See <> for details on the types of connectors provided by {kib}. +When the rule detects the condition, it creates an alert containing the details of the condition. [float] [[alerting-concepts-alerts]] -=== Alerts +== Alerts -When checking for a condition, a rule might identify multiple occurrences of the condition. {kib} tracks each of these *alerts* separately and takes an action per alert. +When checking for a condition, a rule might identify multiple occurrences of the condition. {kib} tracks each of these alerts separately. Depending on the action frequency, an action occurs per alert or at the specified alert summary interval. -Using the server monitoring example, each server with average CPU > 0.9 is tracked as an alert. This means a separate email is sent for each server that exceeds the threshold. +Using the server monitoring example, each server with average CPU > 0.9 is tracked as an alert. This means a separate email is sent for each server that exceeds the threshold whenever the alert status changes. image::images/alerts.svg[{kib} tracks each detected condition as an alert and takes action on each alert] -[float] -[[alerting-concepts-connectors]] -=== Connectors - -Actions often involve connecting with services inside {kib} or integrating with third-party systems. -Rather than repeatedly entering connection information and credentials for each action, {kib} simplifies action setup using connectors. - -Connectors provide a central place to store connection information for services and integrations. For example if four rules send email notifications via the same SMTP service, they can all reference the same SMTP connector. When the SMTP settings change, you can update them once in the connector, instead of having to update four rules. - -image::images/rule-concepts-connectors.svg[Connectors provide a central place to store service connection settings] - [float] == Putting it all together @@ -114,10 +100,10 @@ A rule consists of conditions, actions, and a schedule. When conditions are met, image::images/rule-concepts-summary.svg[Rules, connectors, alerts and actions work together to convert detection into action] -. Anytime a rule's conditions are met, an alert is created. This example checks for servers with average CPU > 0.9. Three servers meet the condition, so three alerts are created. -. Alerts create actions as long as they are not muted or throttled. When actions are created, the template that was setup in the rule is filled with actual values. In this example, three actions are created, and the template string {{server}} is replaced with the server name for each alert. -. {kib} invokes the actions, sending them to a third party integration like an email service. -. If the third party integration has connection parameters or credentials, {kib} will fetch these from the connector referenced in the action. +. Any time a rule's conditions are met, an alert is created. This example checks for servers with average CPU > 0.9. Three servers meet the condition, so three alerts are created. +. Alerts create actions according to the action frequency, as long as they are not muted or throttled. When actions are created, its properties are filled with actual values. In this example, three actions are created when the threshold is met, and the template string {{server}} is replaced with the appropriate server name for each alert. +. {kib} runs the actions, sending notifications by using a third party integration like an email service. +. If the third party integration has connection parameters or credentials, {kib} fetches these from the appropriate connector. [float] [[alerting-concepts-differences]] @@ -135,7 +121,7 @@ Functionally, the {alert-features} differ in that: * Scheduled checks are run on {kib} instead of {es} * {kib} <> through rule types, whereas watches provide low-level control over inputs, conditions, and transformations. * {kib} rules track and persist the state of each detected condition through alerts. This makes it possible to mute and throttle individual alerts, and detect changes in state such as resolution. -* Actions are linked to alerts in Alerting. Actions are fired for each occurrence of a detected condition, rather than for the entire rule. +* Actions are linked to alerts. Actions are fired for each occurrence of a detected condition, rather than for the entire rule. At a higher level, the {alert-features} allow rich integrations across use cases like <>, <>, <>, and <>. Prepackaged rule types simplify setup and hide the details of complex, domain-specific detections, while providing a consistent interface across {kib}. diff --git a/docs/user/alerting/create-and-manage-rules.asciidoc b/docs/user/alerting/create-and-manage-rules.asciidoc index 4e341a00f325b..86716a99f51ab 100644 --- a/docs/user/alerting/create-and-manage-rules.asciidoc +++ b/docs/user/alerting/create-and-manage-rules.asciidoc @@ -22,233 +22,91 @@ available, go to <>. [float] === Required permissions -Access to rules is granted based on your privileges to {alert-features}. For +Access to rules is granted based on your {alert-features} privileges. For more information, go to <>. [float] [[create-edit-rules]] === Create and edit rules -Many rules must be created within the context of a {kib} app like +Some rules must be created within the context of a {kib} app like <>, <>, or <>, but others are generic. Generic rule types can be created in *{rules-ui}* by clicking the *Create rule* button. This will launch a flyout that guides you through selecting -a rule type and configuring its conditions and action type. For details on what -types of rules are available and how to configure them, refer to <>. +a rule type and configuring its conditions and actions. After a rule is created, you can open the action menu (…) and select *Edit rule* to re-open the flyout and change the rule properties. -[float] -[[defining-rules-general-details]] -==== General rule details - -All rules share the following four properties: - -Name:: The name of the rule. While this name does not have to be unique, a -distinctive name can help you identify a rule. -Tags:: A list of tag names that can be applied to a rule. Tags can help you -organize and find rules. -Check every:: Defines how often to evaluate the rule condition. Checks are -queued; they run as close to the defined value as capacity allows. For more -details, go to <>. -Notify:: Defines how often alerts generate actions. Options include running -actions at each check interval, only when the alert status changes, or at a -custom action interval. -+ --- -[[alerting-concepts-suppressing-duplicate-notifications]] -[TIP] -============================================== -Since actions are triggered per alert, a rule can end up generating a large -number of actions. Take the following example where a rule is monitoring three -servers every minute for CPU usage > 0.9, and the rule is set to notify -`On check intervals`: - -* Minute 1: server X123 > 0.9. _One email_ is sent for server X123. -* Minute 2: X123 and Y456 > 0.9. _Two emails_ are sent, one for X123 and one for Y456. -* Minute 3: X123, Y456, Z789 > 0.9. _Three emails_ are sent, one for each of X123, Y456, Z789. - -In this example, three emails are sent for server X123 in the span of 3 minutes -for the same rule. Often, it's desirable to suppress these re-notifications. If -you set the rule notify setting to `On custom action intervals` with an interval -of 5 minutes, you reduce noise by getting emails only every 5 minutes for -servers that continue to exceed the threshold: - -* Minute 1: server X123 > 0.9. _One email_ is sent for server X123. -* Minute 2: X123 and Y456 > 0.9. _One email_ is sent for Y456. -* Minute 3: X123, Y456, Z789 > 0.9. _One email_ is sent for Z789. - -To get notified only once when a server exceeds the threshold, you can set the -rule notify setting to `On status changes`. -============================================== --- - -[role="screenshot"] -image::images/rule-flyout-general-details.png[alt='All rules have name, tags, check every, and notify properties in common'] - [float] [[defining-rules-type-conditions]] ==== Rule type and conditions -Depending upon the {kib} app and context, you might be prompted to choose the type of rule to create. Some apps will preselect the type of rule for you. +Depending on the {kib} app and context, you might be prompted to choose the type of rule to create. Some apps will preselect the type of rule for you. + +Each rule type provides its own way of defining the conditions to detect, but an expression formed by a series of clauses is a common pattern. For example, in an index threshold rule, the `WHEN` clause enables you to select an aggregation operation to apply to a numeric field. [role="screenshot"] -image::images/rule-flyout-rule-type-selection.png[Choosing the type of rule to create] +image::images/rule-flyout-rule-conditions.png[UI for defining rule conditions on an index threshold rule,500] -Each rule type provides its own way of defining the conditions to detect, but an expression formed by a series of clauses is a common pattern. Each clause has a UI control that allows you to define the clause. For example, in an index threshold rule, the `WHEN` clause allows you to select an aggregation operation to apply to a numeric field. +All rules must have a check interval, which defines how often to evaluate the rule conditions. Checks are queued; they run as close to the defined value as capacity allows. -[role="screenshot"] -image::images/rule-flyout-rule-conditions.png[UI for defining rule conditions on an index threshold rule] +For details on what types of rules are available and how to configure them, refer to <>. [float] [[defining-rules-actions-details]] -==== Action type and details - -Actions are optional when you create a rule. However, to receive notifications when a rule meets the defined conditions, you must add one or more actions. Start by selecting a type of connector for your action: - -[role="screenshot"] -image::images/rule-flyout-connector-type-selection.png[UI for selecting an action type] +==== Actions -Each action must specify a <> instance. If no connectors exist for the selected type, click **Add connector** to create one. +You can add one or more actions to your rule to generate notifications when its +conditions are met and when they are no longer met. -After you have selected a connector, use the **Run When** dropdown to choose the action group to associate with this action. When a rule meets the defined condition, it is marked as **Active** and alerts are created and assigned to an action group. In addition to the action groups defined by the selected rule type, each rule also has a **Recovered** action group that is assigned when a rule's conditions are no longer detected. +Each action uses a connector, which provides connection information for a {kib} service or third party integration, depending on where you want to send the notifications. If no connectors exist, click **Add connector** to create one. -Each action type exposes different properties. For example, an email action allows you to set the recipients, the subject, and a message body in markdown format. See <> for details on the types of actions provided by {kib} and their properties. +After you select a connector, set the action frequency. If the rule type supports alert summaries, you can choose to create a summary of alerts on each check interval or on a custom interval. For example, if you create a metrics threshold rule, you can send email notifications that summarize the new, ongoing, and recovered alerts each day: [role="screenshot"] -image::images/rule-flyout-action-details.png[UI for defining an email action] - -You can attach more than one action. Clicking the *Add action* button will prompt you to select another rule type and repeat the above steps again. +image::images/rule-flyout-action-summary.png[UI for defining rule conditions on an index threshold rule,500] -[float] -[[defining-rules-actions-variables]] -===== Action variables +TIP: If you choose a custom action interval, it cannot be shorter than the rule's check interval. -Using the https://mustache.github.io/[Mustache] template syntax `{{variable name}}`, you can pass rule values to an action at the time a condition is detected. You can access the list of available variables using the "add rule variable" button: +Alternatively, you can set the action frequency such that the action runs for each alert. If the rule type does not support alert summaries, this is your only available option. You must choose when the action runs (for example, at each check interval, only when the alert status changes, or at a custom action interval). You must also choose an action group, which affects whether the action runs (for example, the action runs when the issue is detected or when it is recovered). Each rule type has a specific set of valid action groups. [role="screenshot"] -image::images/rule-flyout-action-variables.png[Passing rule values to an action] - -All rule types pass the following variables: - -`date`:: The date the rule scheduled the action, in ISO format. -`kibanaBaseUrl`:: The configured <>. If not configured, this will be empty. -`rule.id`:: The ID of the rule. -`rule.name`:: The name of the rule. -`rule.spaceId`:: The ID of the space for the rule. -`rule.tags`:: The list of tags applied to the rule. +image::images/rule-flyout-action-details.png[UI for defining an email action,500] -There is also a set of action variables specific to the action frequency: +Each connector enables different action properties. For example, an email connector enables you to set the recipients, the subject, and a message body in markdown format. For more information about connectors, refer to <>. -- <> -- <> +[[alerting-concepts-suppressing-duplicate-notifications]] +[TIP] +============================================== +If you are not using alert summaries, actions are triggered per alert and a rule can end up generating a large number of actions. Take the following example where a rule is monitoring three servers every minute for CPU usage > 0.9, and the action frequency is `On check intervals`: -[float] -[[alert-action-variables]] -===== Action variables for each alert +* Minute 1: server X123 > 0.9. _One email_ is sent for server X123. +* Minute 2: X123 and Y456 > 0.9. _Two emails_ are sent, one for X123 and one for Y456. +* Minute 3: X123, Y456, Z789 > 0.9. _Three emails_ are sent, one for each of X123, Y456, Z789. -Although available variables differ by rule type, when the action frequency is -**For each alert**, all rule types pass the following variables: +In this example, three emails are sent for server X123 in the span of 3 minutes for the same rule. Often, it's desirable to suppress these re-notifications. If +you set the action frequency to `On custom action intervals` with an interval of 5 minutes, you reduce noise by getting emails only every 5 minutes for +servers that continue to exceed the threshold: -`alert.actionGroup`:: The ID of the action group of the alert that scheduled the action. -`alert.actionGroupName`:: The name of the action group of the alert that scheduled the action. -`alert.actionSubgroup`:: The action subgroup of the alert that scheduled the action. -`alert.flapping`:: A flag on the alert that indicates whether the alert status is changing repeatedly. -`alert.id`:: The ID of the alert that scheduled the action. +* Minute 1: server X123 > 0.9. _One email_ will be sent for server X123. +* Minute 2: X123 and Y456 > 0.9. _One email_ will be sent for Y456. +* Minute 3: X123, Y456, Z789 > 0.9. _One email_ will be sent for Z789. -[float] -[role="child_attributes"] -[[alert-summary-action-variables]] -===== Action variables for summary of alerts - -NOTE: This type of action frequency is not available for all rule types. - -When the action frequency is **Summary of alerts**, rules pass the following -variables: - -`alerts.all.count`:: The count of all alerts. - -`alerts.all.data`:: -An array of objects for all alerts. The following object properties are examples; it is not a comprehensive list. -+ -.Properties of the alerts.all.data objects -[%collapsible%open] -===== -//# tag::alerts-data[] -`kibana.alert.end`:: Datetime stamp of alert end. preview:[] -`kibana.alert.flapping`:: A flag on the alert that indicates whether the alert status is changing repeatedly. preview:[] -`kibana.alert.instance.id`:: ID of the source that generates the alert. preview:[] -`kibana.alert.reason`:: The reason of the alert (generated with the rule conditions). preview:[] -`kibana.alert.start`:: Datetime stamp of alert start. preview:[] -`kibana.alert.status`:: Alert status (for example, active or OK). preview:[] -//# end::alerts-data[] -===== - -`alerts.new.count`:: The count of new alerts. - -`alerts.new.data`:: -An array of objects for new alerts. The following object properties are examples; it is not a comprehensive list. -+ -.Properties of the alerts.new.data objects -[%collapsible] -===== -include::create-and-manage-rules.asciidoc[tag=alerts-data] -===== - -`alerts.ongoing.count`:: The count of ongoing alerts. - -`alerts.ongoing.data`:: -An array of objects for ongoing alerts. The following object properties are examples; it is not a comprehensive list. -+ -.Properties of the alerts.ongoing.data objects -[%collapsible] -===== -include::create-and-manage-rules.asciidoc[tag=alerts-data] -===== - -`alerts.recovered.count`:: The count of recovered alerts. - -`alerts.recovered.data`:: -An array of objects for recovered alerts. The following object properties are examples; it is not a comprehensive list. -+ -.Properties of the alerts.recovered.data objects -[%collapsible] -===== -include::create-and-manage-rules.asciidoc[tag=alerts-data] -===== - -Some cases exist where the variable values will be "escaped", when used in a context where escaping is needed: - -- For the <> connector, the `message` action configuration property escapes any characters that would be interpreted as Markdown. -- For the <> connector, the `message` action configuration property escapes any characters that would be interpreted as Slack Markdown. -- For the <> connector, the `body` action configuration property escapes any characters that are invalid in JSON string values. - -Mustache also supports "triple braces" of the form `{{{variable name}}}`, which indicates no escaping should be done at all. Care should be used when using this form, as it could end up rendering the variable content in such a way as to make the resulting parameter invalid or formatted incorrectly. +To get notified only once when a server exceeds the threshold, you can set the action frequency to `On status changes`. Alternatively, if the rule type supports alert summaries, consider using them to reduce the volume of notifications. +============================================== [float] -[[defining-rules-actions-variable-context]] -===== Action variable context - -When the action frequency is **For each alert**, each rule type defines additional variables as properties of the variable `context`. For example, if a rule type defines a variable `value`, it can be used in an action parameter as `{{context.value}}`. - -For diagnostic or exploratory purposes, action variables whose values are objects, such as `context`, can be referenced directly as variables. The resulting value will be a JSON representation of the object. For example, if an action parameter includes `{{context}}`, it will expand to the JSON representation of all the variables and values provided by the rule type. To see alert-specific variables, use `{{.}}`. - -For situations where your rule response returns arrays of data, you can loop through the `context`: +[[defining-rules-actions-variables]] +==== Action variables -[source] --------------------------------------------------- -{{#context}}{{.}}{{/context}} --------------------------------------------------- +You can pass rule values to an action at the time a condition is detected. +To view the list of variables available for your rule, click the "add rule variable" button: -For example, looping through search result hits: +[role="screenshot"] +image::images/rule-flyout-action-variables.png[Passing rule values to an action,500] -[source] --------------------------------------------------- -triggering data was: -{{#context.hits}} - {{_source.message}} -{{/context.hits}} --------------------------------------------------- +For more information about common action variables, refer to <>. [float] [[controlling-rules]] @@ -298,7 +156,7 @@ Some rule types cannot be exported through this interface: Rules are disabled on export. You are prompted to re-enable the rule on successful import. [role="screenshot"] -image::images/rules-imported-banner.png[Rules import banner, width=50%] +image::images/rules-imported-banner.png[Rules import banner,500] [float] [[rule-details]] diff --git a/docs/user/alerting/images/alerts.svg b/docs/user/alerting/images/alerts.svg index 022b3106ae802..5e7819dd583f1 100644 --- a/docs/user/alerting/images/alerts.svg +++ b/docs/user/alerting/images/alerts.svg @@ -1 +1,62 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/user/alerting/images/rule-concepts-connectors.svg b/docs/user/alerting/images/rule-concepts-connectors.svg deleted file mode 100644 index caee5f858fea9..0000000000000 --- a/docs/user/alerting/images/rule-concepts-connectors.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/docs/user/alerting/images/rule-concepts-summary.svg b/docs/user/alerting/images/rule-concepts-summary.svg index aed7020b9d3e2..d7fd2c5806b39 100644 --- a/docs/user/alerting/images/rule-concepts-summary.svg +++ b/docs/user/alerting/images/rule-concepts-summary.svg @@ -1 +1,109 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/user/alerting/images/rule-flyout-action-details.png b/docs/user/alerting/images/rule-flyout-action-details.png index d59d58dc34af3..902ab224e21af 100644 Binary files a/docs/user/alerting/images/rule-flyout-action-details.png and b/docs/user/alerting/images/rule-flyout-action-details.png differ diff --git a/docs/user/alerting/images/rule-flyout-action-summary.png b/docs/user/alerting/images/rule-flyout-action-summary.png new file mode 100644 index 0000000000000..f6fe3ba1ee9f5 Binary files /dev/null and b/docs/user/alerting/images/rule-flyout-action-summary.png differ diff --git a/docs/user/alerting/images/rule-flyout-action-variables.png b/docs/user/alerting/images/rule-flyout-action-variables.png index d24a11fe73ec3..9bbc91ef24f8c 100644 Binary files a/docs/user/alerting/images/rule-flyout-action-variables.png and b/docs/user/alerting/images/rule-flyout-action-variables.png differ diff --git a/docs/user/alerting/images/rule-flyout-connector-type-selection.png b/docs/user/alerting/images/rule-flyout-connector-type-selection.png deleted file mode 100644 index de431b36c5c32..0000000000000 Binary files a/docs/user/alerting/images/rule-flyout-connector-type-selection.png and /dev/null differ diff --git a/docs/user/alerting/images/rule-flyout-general-details.png b/docs/user/alerting/images/rule-flyout-general-details.png deleted file mode 100644 index 40806d1a5007a..0000000000000 Binary files a/docs/user/alerting/images/rule-flyout-general-details.png and /dev/null differ diff --git a/docs/user/alerting/images/rule-flyout-rule-conditions.png b/docs/user/alerting/images/rule-flyout-rule-conditions.png index 2065cbd117b75..ffdf870dfa001 100644 Binary files a/docs/user/alerting/images/rule-flyout-rule-conditions.png and b/docs/user/alerting/images/rule-flyout-rule-conditions.png differ diff --git a/docs/user/alerting/images/rule-flyout-rule-type-selection.png b/docs/user/alerting/images/rule-flyout-rule-type-selection.png deleted file mode 100644 index 5a086a4ea6970..0000000000000 Binary files a/docs/user/alerting/images/rule-flyout-rule-type-selection.png and /dev/null differ diff --git a/docs/user/alerting/images/what-is-a-rule.svg b/docs/user/alerting/images/what-is-a-rule.svg index 2117e448ba136..2f89c2b4edd59 100644 --- a/docs/user/alerting/images/what-is-a-rule.svg +++ b/docs/user/alerting/images/what-is-a-rule.svg @@ -1 +1,24 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/user/alerting/images/what-is-an-action.svg b/docs/user/alerting/images/what-is-an-action.svg deleted file mode 100644 index f8435ee24fc19..0000000000000 --- a/docs/user/alerting/images/what-is-an-action.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/docs/user/alerting/index.asciidoc b/docs/user/alerting/index.asciidoc index 957d99a54ebaa..b2b2082ce71f7 100644 --- a/docs/user/alerting/index.asciidoc +++ b/docs/user/alerting/index.asciidoc @@ -2,4 +2,5 @@ include::alerting-getting-started.asciidoc[] include::alerting-setup.asciidoc[] include::create-and-manage-rules.asciidoc[] include::rule-types.asciidoc[] +include::action-variables.asciidoc[] include::alerting-troubleshooting.asciidoc[] diff --git a/docs/user/alerting/rule-types/es-query.asciidoc b/docs/user/alerting/rule-types/es-query.asciidoc index 9b63dc4aaaccb..ccd8128020f56 100644 --- a/docs/user/alerting/rule-types/es-query.asciidoc +++ b/docs/user/alerting/rule-types/es-query.asciidoc @@ -9,7 +9,7 @@ threshold condition is met. [float] === Create the rule -Fill in the <>, then select +Fill in the name and optional tags, then select *{es} query*. {es} query rule can be defined using KQL/Lucene or Query DSL. [float] @@ -30,8 +30,7 @@ Threshold:: Defines a threshold value and a comparison operator (`is above`, calculated by the aggregation is compared to this threshold. Time window:: Defines how far back to search for documents, using the *time field* set in the *index* clause. Generally this value should be set to a -value higher than the *check every* value in the -<>, to avoid gaps in +value higher than the *check every* value, to avoid gaps in detection. Size:: Specifies the number of documents to pass to the configured actions when the threshold condition is met. diff --git a/docs/user/alerting/rule-types/geo-rule-types.asciidoc b/docs/user/alerting/rule-types/geo-rule-types.asciidoc index 454c51ad69860..755bf5837ea59 100644 --- a/docs/user/alerting/rule-types/geo-rule-types.asciidoc +++ b/docs/user/alerting/rule-types/geo-rule-types.asciidoc @@ -29,7 +29,7 @@ than the current time minus the amount of the interval. If data older than [float] ==== Create the rule -Fill in the <>, then select Tracking containment. +Fill in the name and optional tags, then select Tracking containment. [float] ==== Define the conditions diff --git a/docs/user/alerting/rule-types/index-threshold.asciidoc b/docs/user/alerting/rule-types/index-threshold.asciidoc index b19dc83ae0351..870c53f69b9a1 100644 --- a/docs/user/alerting/rule-types/index-threshold.asciidoc +++ b/docs/user/alerting/rule-types/index-threshold.asciidoc @@ -7,7 +7,7 @@ The index threshold rule type runs an {es} query. It aggregates field values fro [float] ==== Create the rule -Fill in the <>, then select *Index Threshold*. +Fill in the name and optional tags, then select *Index Threshold*. [float] ==== Define the conditions @@ -21,7 +21,7 @@ Index:: This clause requires an *index or data view* and a *time field* that wil When:: This clause specifies how the value to be compared to the threshold is calculated. The value is calculated by aggregating a numeric field a the *time window*. The aggregation options are: `count`, `average`, `sum`, `min`, and `max`. When using `count` the document count is used, and an aggregation field is not necessary. Over/Grouped Over:: This clause lets you configure whether the aggregation is applied over all documents, or should be split into groups using a grouping field. If grouping is used, an <> will be created for each group when it exceeds the threshold. To limit the number of alerts on high cardinality fields, you must specify the number of groups to check against the threshold. Only the *top* groups are checked. Threshold:: This clause defines a threshold value and a comparison operator (one of `is above`, `is above or equals`, `is below`, `is below or equals`, or `is between`). The result of the aggregation is compared to this threshold. -Time window:: This clause determines how far back to search for documents, using the *time field* set in the *index* clause. Generally this value should be to a value higher than the *check every* value in the <>, to avoid gaps in detection. +Time window:: This clause determines how far back to search for documents, using the *time field* set in the *index* clause. Generally this value should be to a value higher than the *check every* value, to avoid gaps in detection. If data is available and all clauses have been defined, a preview chart will render the threshold value and display a line chart showing the value for the last 30 intervals. This can provide an indication of recent values and their proximity to the threshold, and help you tune the clauses. diff --git a/docs/user/production-considerations/alerting-production-considerations.asciidoc b/docs/user/production-considerations/alerting-production-considerations.asciidoc index f94ec1c3d04bb..e3d343475e175 100644 --- a/docs/user/production-considerations/alerting-production-considerations.asciidoc +++ b/docs/user/production-considerations/alerting-production-considerations.asciidoc @@ -19,7 +19,7 @@ When relying on rules and actions as mission critical services, make sure you fo By default, each {kib} instance polls for work at three second intervals, and can run a maximum of ten concurrent tasks. These tasks are then run on the {kib} server. -Rules are recurring background tasks which are rescheduled according to the <> on completion. +Rules are recurring background tasks which are rescheduled according to the check interval on completion. Actions are non-recurring background tasks which are deleted on completion. For more details on Task Manager, see <>. @@ -42,8 +42,8 @@ As rules and actions leverage background tasks to perform the majority of work, When estimating the required task throughput, keep the following in mind: -* Each rule uses a single recurring task that is scheduled to run at the cadence defined by its <>. -* Each action uses a single task. However, because <>, alerts can generate a large number of non-recurring tasks. +* Each rule uses a single recurring task that is scheduled to run at the cadence defined by its check interval. +* Each action uses a single task. However, because actions are taken per instance, alerts can generate a large number of non-recurring tasks. It is difficult to predict how much throughput is needed to ensure all rules and actions are executed at consistent schedules. By counting rules as recurring tasks and actions as non-recurring tasks, a rough throughput <> as a _tasks per minute_ measurement. diff --git a/src/plugins/content_management/.storybook/main.js b/examples/content_management_examples/.storybook/main.js similarity index 100% rename from src/plugins/content_management/.storybook/main.js rename to examples/content_management_examples/.storybook/main.js diff --git a/examples/content_management_examples/README.md b/examples/content_management_examples/README.md new file mode 100644 index 0000000000000..a4b0486903dba --- /dev/null +++ b/examples/content_management_examples/README.md @@ -0,0 +1,3 @@ +# Content Management Examples + +An example plugin that shows how to integrate with the Kibana "content management" plugin. diff --git a/packages/shared-ux/button_toolbar/src/buttons/primary/index.ts b/examples/content_management_examples/common/examples/todos/index.ts similarity index 77% rename from packages/shared-ux/button_toolbar/src/buttons/primary/index.ts rename to examples/content_management_examples/common/examples/todos/index.ts index 1d3220b3654aa..9ef986ec2032f 100644 --- a/packages/shared-ux/button_toolbar/src/buttons/primary/index.ts +++ b/examples/content_management_examples/common/examples/todos/index.ts @@ -6,5 +6,4 @@ * Side Public License, v 1. */ -export { PrimaryButton } from './primary'; -export type { Props as PrimaryButtonProps } from './primary'; +export * from './todos'; diff --git a/examples/content_management_examples/common/examples/todos/todos.ts b/examples/content_management_examples/common/examples/todos/todos.ts new file mode 100644 index 0000000000000..0371651c128f9 --- /dev/null +++ b/examples/content_management_examples/common/examples/todos/todos.ts @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { schema } from '@kbn/config-schema'; +import { + CreateIn, + DeleteIn, + GetIn, + SearchIn, + UpdateIn, +} from '@kbn/content-management-plugin/common'; + +export const TODO_CONTENT_ID = 'todos'; +export interface Todo { + id: string; + title: string; + completed: boolean; +} +const todoSchema = schema.object({ + id: schema.string(), + title: schema.string(), + completed: schema.boolean(), +}); + +export type TodoCreateIn = CreateIn<'todos', { title: string }>; +export type TodoCreateOut = Todo; // TODO: Is this correct? +export const createInSchema = schema.object({ title: schema.string() }); +export const createOutSchema = todoSchema; + +export type TodoUpdateIn = UpdateIn<'todos', Partial>>; +export type TodoUpdateOut = Todo; +export const updateInSchema = schema.object({ + title: schema.maybe(schema.string()), + completed: schema.maybe(schema.boolean()), +}); +export const updateOutSchema = todoSchema; + +export type TodoDeleteIn = DeleteIn<'todos', { id: string }>; +export type TodoDeleteOut = void; + +export type TodoGetIn = GetIn<'todos'>; +export type TodoGetOut = Todo; +export const getOutSchema = todoSchema; + +export type TodoSearchIn = SearchIn<'todos', { filter?: 'todo' | 'completed' }>; +export interface TodoSearchOut { + hits: Todo[]; +} +export const searchInSchema = schema.object({ + filter: schema.maybe( + schema.oneOf([schema.literal('todo'), schema.literal('completed')], { + defaultValue: undefined, + }) + ), +}); +export const searchOutSchema = schema.object({ + hits: schema.arrayOf(todoSchema), +}); diff --git a/examples/content_management_examples/jest.config.js b/examples/content_management_examples/jest.config.js new file mode 100644 index 0000000000000..9f84c97053475 --- /dev/null +++ b/examples/content_management_examples/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/examples/content_management_examples'], +}; diff --git a/examples/content_management_examples/kibana.jsonc b/examples/content_management_examples/kibana.jsonc new file mode 100644 index 0000000000000..24759d7abdfb6 --- /dev/null +++ b/examples/content_management_examples/kibana.jsonc @@ -0,0 +1,15 @@ +{ + "type": "plugin", + "id": "@kbn/content-management-examples-plugin", + "owner": "@elastic/appex-sharedux", + "description": "Example plugin integrating with content management plugin", + "plugin": { + "id": "contentManagementExamples", + "server": true, + "browser": true, + "requiredPlugins": [ + "contentManagement", + "developerExamples" + ] + } +} diff --git a/examples/content_management_examples/public/examples/index.tsx b/examples/content_management_examples/public/examples/index.tsx new file mode 100644 index 0000000000000..715f3a6809581 --- /dev/null +++ b/examples/content_management_examples/public/examples/index.tsx @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import ReactDOM from 'react-dom'; +import { EuiPageTemplate } from '@elastic/eui'; +import { AppMountParameters, CoreStart } from '@kbn/core/public'; +import { StartDeps } from '../types'; +import { TodoApp } from './todos'; + +export const renderApp = ( + { notifications }: CoreStart, + { contentManagement }: StartDeps, + { element }: AppMountParameters +) => { + ReactDOM.render( + + + + + , + element + ); + + return () => ReactDOM.unmountComponentAtNode(element); +}; diff --git a/examples/content_management_examples/public/examples/todos/index.tsx b/examples/content_management_examples/public/examples/todos/index.tsx new file mode 100644 index 0000000000000..dd19feb3e0a80 --- /dev/null +++ b/examples/content_management_examples/public/examples/todos/index.tsx @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { TodoApp } from './todo_app'; diff --git a/src/plugins/content_management/demo/todo/todo.stories.test.tsx b/examples/content_management_examples/public/examples/todos/stories/todo.stories.test.tsx similarity index 100% rename from src/plugins/content_management/demo/todo/todo.stories.test.tsx rename to examples/content_management_examples/public/examples/todos/stories/todo.stories.test.tsx diff --git a/src/plugins/content_management/demo/todo/todo.stories.tsx b/examples/content_management_examples/public/examples/todos/stories/todo.stories.tsx similarity index 88% rename from src/plugins/content_management/demo/todo/todo.stories.tsx rename to examples/content_management_examples/public/examples/todos/stories/todo.stories.tsx index 23cd2a194cf6d..5b7b8f1e23823 100644 --- a/src/plugins/content_management/demo/todo/todo.stories.tsx +++ b/examples/content_management_examples/public/examples/todos/stories/todo.stories.tsx @@ -7,8 +7,9 @@ */ import * as React from 'react'; -import { Todos } from './todos'; -import { ContentClientProvider, ContentClient } from '../../public/content_client'; +import { ContentClientProvider, ContentClient } from '@kbn/content-management-plugin/public'; + +import { Todos } from '../todos'; import { TodosClient } from './todos_client'; export default { diff --git a/src/plugins/content_management/demo/todo/todos_client.ts b/examples/content_management_examples/public/examples/todos/stories/todos_client.ts similarity index 62% rename from src/plugins/content_management/demo/todo/todos_client.ts rename to examples/content_management_examples/public/examples/todos/stories/todos_client.ts index c6f5fe4bf5f36..04d1f6fb990ed 100644 --- a/src/plugins/content_management/demo/todo/todos_client.ts +++ b/examples/content_management_examples/public/examples/todos/stories/todos_client.ts @@ -7,28 +7,32 @@ */ import { v4 as uuidv4 } from 'uuid'; -import type { CrudClient } from '../../public/crud_client'; -import type { CreateIn, DeleteIn, GetIn, SearchIn, UpdateIn } from '../../common'; - -export interface Todo { - id: string; - title: string; - completed: boolean; -} - -export type TodoCreateIn = CreateIn<'todos', { title: string }>; -export type TodoUpdateIn = UpdateIn<'todos', Partial>>; -export type TodoDeleteIn = DeleteIn<'todos', { id: string }>; -export type TodoGetIn = GetIn<'todos'>; -export type TodoSearchIn = SearchIn<'todos', { filter?: 'todo' | 'completed' }>; +import type { CrudClient } from '@kbn/content-management-plugin/public'; +import type { + TodoCreateIn, + TodoUpdateIn, + TodoDeleteIn, + TodoGetIn, + TodoSearchIn, + TodoUpdateOut, + TodoCreateOut, + TodoSearchOut, + TodoDeleteOut, + Todo, + TodoGetOut, +} from '../../../../common/examples/todos'; +/** + * This client is used in the storybook examples to simulate a server-side registry client + * and to show how a content type can have a custom client-side CRUD client without using the server-side registry + */ export class TodosClient implements CrudClient { private todos: Todo[] = [ { id: uuidv4(), title: 'Learn Elasticsearch', completed: true }, { id: uuidv4(), title: 'Learn Kibana', completed: false }, ]; - async create(input: TodoCreateIn): Promise { + async create(input: TodoCreateIn): Promise { const todo = { id: uuidv4(), title: input.data.title, @@ -38,22 +42,22 @@ export class TodosClient implements CrudClient { return todo; } - async delete(input: TodoDeleteIn): Promise { + async delete(input: TodoDeleteIn): Promise { this.todos = this.todos.filter((todo) => todo.id !== input.id); } - async get(input: TodoGetIn): Promise { + async get(input: TodoGetIn): Promise { return this.todos.find((todo) => todo.id === input.id)!; } - async search(input: TodoSearchIn): Promise<{ hits: Todo[] }> { + async search(input: TodoSearchIn): Promise { const filter = input.query.filter; if (filter === 'todo') return { hits: this.todos.filter((t) => !t.completed) }; if (filter === 'completed') return { hits: this.todos.filter((t) => t.completed) }; return { hits: [...this.todos] }; } - async update(input: TodoUpdateIn): Promise { + async update(input: TodoUpdateIn): Promise { const idToUpdate = input.id; const todoToUpdate = this.todos.find((todo) => todo.id === idToUpdate)!; if (todoToUpdate) { diff --git a/src/plugins/presentation_util/public/components/solution_toolbar/items/primary_popover.tsx b/examples/content_management_examples/public/examples/todos/todo_app.tsx similarity index 53% rename from src/plugins/presentation_util/public/components/solution_toolbar/items/primary_popover.tsx rename to examples/content_management_examples/public/examples/todos/todo_app.tsx index 164d4c9b4a1a6..d2fae22bb41a1 100644 --- a/src/plugins/presentation_util/public/components/solution_toolbar/items/primary_popover.tsx +++ b/examples/content_management_examples/public/examples/todos/todo_app.tsx @@ -7,11 +7,13 @@ */ import React from 'react'; +import { ContentClientProvider, type ContentClient } from '@kbn/content-management-plugin/public'; +import { Todos } from './todos'; -import { SolutionToolbarPopover, Props as SolutionToolbarPopoverProps } from './popover'; - -export type Props = Omit; - -export const PrimaryActionPopover = (props: Omit) => ( - -); +export const TodoApp = (props: { contentClient: ContentClient }) => { + return ( + + + + ); +}; diff --git a/src/plugins/content_management/demo/todo/todos.tsx b/examples/content_management_examples/public/examples/todos/todos.tsx similarity index 80% rename from src/plugins/content_management/demo/todo/todos.tsx rename to examples/content_management_examples/public/examples/todos/todos.tsx index 667e455029ab4..84c48aa24c88b 100644 --- a/src/plugins/content_management/demo/todo/todos.tsx +++ b/examples/content_management_examples/public/examples/todos/todos.tsx @@ -7,22 +7,32 @@ */ import React from 'react'; import { EuiButtonGroup, EuiButtonIcon, EuiCheckbox, EuiFieldText, EuiSpacer } from '@elastic/eui'; - import { useCreateContentMutation, useDeleteContentMutation, useSearchContentQuery, useUpdateContentMutation, - // eslint-disable-next-line @kbn/imports/no_boundary_crossing -} from '../../public/content_client'; -import type { Todo, TodoCreateIn, TodoDeleteIn, TodoSearchIn, TodoUpdateIn } from './todos_client'; +} from '@kbn/content-management-plugin/public'; + +import { + TODO_CONTENT_ID, + Todo, + TodoCreateIn, + TodoDeleteIn, + TodoSearchIn, + TodoUpdateIn, + TodoUpdateOut, + TodoCreateOut, + TodoSearchOut, + TodoDeleteOut, +} from '../../../common/examples/todos'; -const useCreateTodoMutation = () => useCreateContentMutation(); -const useDeleteTodoMutation = () => useDeleteContentMutation(); -const useUpdateTodoMutation = () => useUpdateContentMutation(); +const useCreateTodoMutation = () => useCreateContentMutation(); +const useDeleteTodoMutation = () => useDeleteContentMutation(); +const useUpdateTodoMutation = () => useUpdateContentMutation(); const useSearchTodosQuery = ({ filter }: { filter: TodoSearchIn['query']['filter'] }) => - useSearchContentQuery({ - contentTypeId: 'todos', + useSearchContentQuery({ + contentTypeId: TODO_CONTENT_ID, query: { filter }, }); @@ -70,14 +80,17 @@ export const Todos = () => {
    {data.hits.map((todo: Todo) => ( -
  • +
  • { updateTodoMutation.mutate({ - contentTypeId: 'todos', + contentTypeId: TODO_CONTENT_ID, id: todo.id, data: { completed: e.target.checked, @@ -85,7 +98,7 @@ export const Todos = () => { }); }} label={todo.title} - data-test-subj={`todoCheckbox-${todo.id}`} + data-test-subj={`todoCheckbox todoCheckbox-${todo.id}`} /> { aria-label="Delete" color="danger" onClick={() => { - deleteTodoMutation.mutate({ contentTypeId: 'todos', id: todo.id }); + deleteTodoMutation.mutate({ contentTypeId: TODO_CONTENT_ID, id: todo.id }); }} />
  • @@ -112,7 +125,7 @@ export const Todos = () => { if (!inputRef || !inputRef.value) return; createTodoMutation.mutate({ - contentTypeId: 'todos', + contentTypeId: TODO_CONTENT_ID, data: { title: inputRef.value, }, diff --git a/src/plugins/files/common/register_default_file_kinds.ts b/examples/content_management_examples/public/index.ts similarity index 60% rename from src/plugins/files/common/register_default_file_kinds.ts rename to examples/content_management_examples/public/index.ts index 37982c445cd16..ba96df2040435 100644 --- a/src/plugins/files/common/register_default_file_kinds.ts +++ b/examples/content_management_examples/public/index.ts @@ -6,10 +6,8 @@ * Side Public License, v 1. */ -import { getFileKindsRegistry } from './file_kinds_registry'; -import { defaultImageFileKind } from '.'; +import { ContentManagementExamplesPlugin } from './plugin'; -export function registerDefaultFileKinds() { - const registry = getFileKindsRegistry(); - registry.register(defaultImageFileKind); +export function plugin() { + return new ContentManagementExamplesPlugin(); } diff --git a/examples/content_management_examples/public/plugin.ts b/examples/content_management_examples/public/plugin.ts new file mode 100644 index 0000000000000..8731ef667650a --- /dev/null +++ b/examples/content_management_examples/public/plugin.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 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 { AppNavLinkStatus } from '@kbn/core-application-browser'; +import { AppMountParameters, CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; +import { StartDeps, SetupDeps } from './types'; + +export class ContentManagementExamplesPlugin + implements Plugin +{ + public setup(core: CoreSetup, { contentManagement, developerExamples }: SetupDeps) { + developerExamples.register({ + appId: `contentManagementExamples`, + title: `Content Management Examples`, + description: 'Example plugin for the content management plugin', + }); + + core.application.register({ + id: `contentManagementExamples`, + title: `Content Management Examples`, + navLinkStatus: AppNavLinkStatus.hidden, + async mount(params: AppMountParameters) { + const { renderApp } = await import('./examples'); + const [coreStart, deps] = await core.getStartServices(); + return renderApp(coreStart, deps, params); + }, + }); + + return {}; + } + + public start(core: CoreStart) { + return {}; + } + + public stop() {} +} diff --git a/examples/content_management_examples/public/types.ts b/examples/content_management_examples/public/types.ts new file mode 100644 index 0000000000000..83e65c5455afd --- /dev/null +++ b/examples/content_management_examples/public/types.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { + ContentManagementPublicSetup, + ContentManagementPublicStart, +} from '@kbn/content-management-plugin/public'; +import { DeveloperExamplesSetup } from '@kbn/developer-examples-plugin/public'; + +export interface SetupDeps { + contentManagement: ContentManagementPublicSetup; + developerExamples: DeveloperExamplesSetup; +} + +export interface StartDeps { + contentManagement: ContentManagementPublicStart; +} diff --git a/examples/content_management_examples/server/examples/todos/index.ts b/examples/content_management_examples/server/examples/todos/index.ts new file mode 100644 index 0000000000000..bf065ba480736 --- /dev/null +++ b/examples/content_management_examples/server/examples/todos/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { registerTodoContentType } from './todos'; diff --git a/examples/content_management_examples/server/examples/todos/todos.ts b/examples/content_management_examples/server/examples/todos/todos.ts new file mode 100644 index 0000000000000..fe8a7826be655 --- /dev/null +++ b/examples/content_management_examples/server/examples/todos/todos.ts @@ -0,0 +1,147 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { + ContentStorage, + StorageContext, + ContentManagementServerSetup, +} from '@kbn/content-management-plugin/server'; +import { v4 } from 'uuid'; +import { + createInSchema, + searchInSchema, + Todo, + TODO_CONTENT_ID, + updateInSchema, + TodoSearchOut, + TodoCreateOut, + TodoUpdateOut, + TodoDeleteOut, + TodoGetOut, + createOutSchema, + getOutSchema, + updateOutSchema, + searchOutSchema, + TodoUpdateIn, + TodoSearchIn, + TodoCreateIn, +} from '../../../common/examples/todos'; + +export const registerTodoContentType = ({ + contentManagement, +}: { + contentManagement: ContentManagementServerSetup; +}) => { + contentManagement.register({ + id: TODO_CONTENT_ID, + schemas: { + content: { + create: { + in: { + data: createInSchema, + }, + out: { + result: createOutSchema, + }, + }, + update: { + in: { + data: updateInSchema, + }, + out: { + result: updateOutSchema, + }, + }, + search: { + in: { + query: searchInSchema, + }, + out: { + result: searchOutSchema, + }, + }, + get: { + out: { + result: getOutSchema, + }, + }, + }, + }, + storage: new TodosStorage(), + }); +}; + +class TodosStorage implements ContentStorage { + private db: Map = new Map(); + + constructor() { + const id1 = v4(); + this.db.set(id1, { + id: id1, + title: 'Learn Elasticsearch', + completed: true, + }); + const id2 = v4(); + this.db.set(id2, { + id: id2, + title: 'Learn Kibana', + completed: false, + }); + } + + async get(ctx: StorageContext, id: string): Promise { + return this.db.get(id)!; + } + + async bulkGet(ctx: StorageContext, ids: string[]): Promise { + return ids.map((id) => this.db.get(id)!); + } + + async create(ctx: StorageContext, data: TodoCreateIn['data']): Promise { + const todo: Todo = { + ...data, + completed: false, + id: v4(), + }; + + this.db.set(todo.id, todo); + + return todo; + } + + async update( + ctx: StorageContext, + id: string, + data: TodoUpdateIn['data'] + ): Promise { + const content = this.db.get(id); + if (!content) { + throw new Error(`Content to update not found [${id}].`); + } + + const updatedContent = { + ...content, + ...data, + }; + + this.db.set(id, updatedContent); + + return updatedContent; + } + + async delete(ctx: StorageContext, id: string): Promise { + this.db.delete(id); + } + + async search(ctx: StorageContext, query: TodoSearchIn['query']): Promise { + const hits = Array.from(this.db.values()); + if (query.filter === 'todo') return { hits: hits.filter((t) => !t.completed) }; + if (query.filter === 'completed') return { hits: hits.filter((t) => t.completed) }; + return { hits }; + } +} diff --git a/examples/content_management_examples/server/index.ts b/examples/content_management_examples/server/index.ts new file mode 100644 index 0000000000000..d75af7bacf224 --- /dev/null +++ b/examples/content_management_examples/server/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { PluginInitializerContext } from '@kbn/core/server'; +import { ContentManagementExamplesPlugin } from './plugin'; + +export function plugin(initializerContext: PluginInitializerContext) { + return new ContentManagementExamplesPlugin(initializerContext); +} diff --git a/examples/content_management_examples/server/plugin.ts b/examples/content_management_examples/server/plugin.ts new file mode 100644 index 0000000000000..bf66de46e1251 --- /dev/null +++ b/examples/content_management_examples/server/plugin.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from '@kbn/core/server'; +import type { SetupDeps, StartDeps } from './types'; +import { registerTodoContentType } from './examples/todos'; + +export class ContentManagementExamplesPlugin implements Plugin { + constructor(initializerContext: PluginInitializerContext) {} + + public setup(core: CoreSetup, { contentManagement }: SetupDeps) { + registerTodoContentType({ contentManagement }); + return {}; + } + + public start(core: CoreStart, { contentManagement }: StartDeps) { + return {}; + } + + public stop() {} +} diff --git a/examples/content_management_examples/server/types.ts b/examples/content_management_examples/server/types.ts new file mode 100644 index 0000000000000..57427001461b0 --- /dev/null +++ b/examples/content_management_examples/server/types.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { + ContentManagementServerSetup, + ContentManagementServerStart, +} from '@kbn/content-management-plugin/server'; + +export interface SetupDeps { + contentManagement: ContentManagementServerSetup; +} + +export interface StartDeps { + contentManagement: ContentManagementServerStart; +} diff --git a/examples/content_management_examples/tsconfig.json b/examples/content_management_examples/tsconfig.json new file mode 100644 index 0000000000000..1e899345b0bf4 --- /dev/null +++ b/examples/content_management_examples/tsconfig.json @@ -0,0 +1,24 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types" + }, + "include": [ + "index.ts", + "common/**/*", + "public/**/*.ts", + "public/**/*.tsx", + "server/**/*.ts", + "../../typings/**/*" + ], + "exclude": [ + "target/**/*", + ], + "kbn_references": [ + "@kbn/core", + "@kbn/developer-examples-plugin", + "@kbn/config-schema", + "@kbn/content-management-plugin", + "@kbn/core-application-browser", + ] +} diff --git a/examples/controls_example/public/add_button_example.tsx b/examples/controls_example/public/add_button_example.tsx new file mode 100644 index 0000000000000..a1603bf66606e --- /dev/null +++ b/examples/controls_example/public/add_button_example.tsx @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; + +import { ViewMode } from '@kbn/embeddable-plugin/public'; +import { withSuspense } from '@kbn/presentation-util-plugin/public'; +import { EuiPanel, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui'; +import { LazyControlGroupRenderer } from '@kbn/controls-plugin/public'; + +const ControlGroupRenderer = withSuspense(LazyControlGroupRenderer); + +export const AddButtonExample = ({ dataViewId }: { dataViewId: string }) => { + return ( + <> + +

    Add button example

    +
    + +

    + Use the built in add button to add controls to a control group based on a hardcoded + dataViewId and a simplified editor flyout +

    +
    + + + { + await builder.addDataControlFromField(initialInput, { + dataViewId, + title: 'Destintion', + fieldName: 'geo.dest', + grow: false, + width: 'small', + }); + await builder.addDataControlFromField(initialInput, { + dataViewId, + fieldName: 'geo.src', + grow: false, + title: 'Source', + width: 'small', + }); + return { + initialInput: { + ...initialInput, + viewMode: ViewMode.EDIT, + defaultControlGrow: false, + defaultControlWidth: 'small', + }, + settings: { + showAddButton: true, + staticDataViewId: dataViewId, + editorConfig: { + hideAdditionalSettings: true, + hideDataViewSelector: true, + hideWidthSettings: true, + }, + }, + }; + }} + /> + + + ); +}; diff --git a/examples/controls_example/public/app.tsx b/examples/controls_example/public/app.tsx index 6b1f9ddb711df..55ce24ca5fd34 100644 --- a/examples/controls_example/public/app.tsx +++ b/examples/controls_example/public/app.tsx @@ -16,6 +16,7 @@ import { ControlsExampleStartDeps } from './plugin'; import { BasicReduxExample } from './basic_redux_example'; import { EditExample } from './edit_example'; import { SearchExample } from './search_example'; +import { AddButtonExample } from './add_button_example'; export const renderApp = async ( { data, navigation }: ControlsExampleStartDeps, @@ -30,6 +31,8 @@ export const renderApp = async ( + + ) : (
    {'Install web logs sample data to run controls examples.'}
    diff --git a/examples/controls_example/public/basic_redux_example.tsx b/examples/controls_example/public/basic_redux_example.tsx index 0586882d251b2..4500aa9aaf4bf 100644 --- a/examples/controls_example/public/basic_redux_example.tsx +++ b/examples/controls_example/public/basic_redux_example.tsx @@ -80,7 +80,7 @@ export const BasicReduxExample = ({ dataViewId }: { dataViewId: string }) => { onLoadComplete={async (newControlGroup) => { setControlGroup(newControlGroup); }} - getInitialInput={async (initialInput, builder) => { + getCreationOptions={async (initialInput, builder) => { await builder.addDataControlFromField(initialInput, { dataViewId, title: 'Destintion country', @@ -96,8 +96,10 @@ export const BasicReduxExample = ({ dataViewId }: { dataViewId: string }) => { title: 'Bytes', }); return { - ...initialInput, - viewMode: ViewMode.VIEW, + initialInput: { + ...initialInput, + viewMode: ViewMode.VIEW, + }, }; }} /> diff --git a/examples/controls_example/public/edit_example.tsx b/examples/controls_example/public/edit_example.tsx index 5278c16f8c85a..d8bfd515ca7da 100644 --- a/examples/controls_example/public/edit_example.tsx +++ b/examples/controls_example/public/edit_example.tsx @@ -104,12 +104,14 @@ export const EditExample = () => { ) : null} { + getCreationOptions={async (initialInput, builder) => { const persistedInput = await onLoad(); return { - ...initialInput, - ...persistedInput, - viewMode: ViewMode.EDIT, + initialInput: { + ...initialInput, + ...persistedInput, + viewMode: ViewMode.EDIT, + }, }; }} onLoadComplete={async (newControlGroup) => { diff --git a/examples/controls_example/public/search_example.tsx b/examples/controls_example/public/search_example.tsx index db2a291af1a0d..f9ca9d8e24eca 100644 --- a/examples/controls_example/public/search_example.tsx +++ b/examples/controls_example/public/search_example.tsx @@ -133,7 +133,7 @@ export const SearchExample = ({ data, dataView, navigation }: Props) => { /> { + getCreationOptions={async (initialInput, builder) => { await builder.addDataControlFromField(initialInput, { dataViewId: dataView.id!, title: 'Destintion country', @@ -149,8 +149,10 @@ export const SearchExample = ({ data, dataView, navigation }: Props) => { title: 'Bytes', }); return { - ...initialInput, - viewMode: ViewMode.VIEW, + initialInput: { + ...initialInput, + viewMode: ViewMode.VIEW, + }, }; }} onLoadComplete={async (newControlGroup) => { diff --git a/examples/files_example/public/plugin.ts b/examples/files_example/public/plugin.ts index ba0b1dfd54378..b36928e5f5566 100644 --- a/examples/files_example/public/plugin.ts +++ b/examples/files_example/public/plugin.ts @@ -18,7 +18,10 @@ export class FilesExamplePlugin core: CoreSetup, { files, developerExamples }: FilesExamplePluginsSetup ) { - files.registerFileKind(exampleFileKind); + files.registerFileKind({ + id: exampleFileKind.id, + allowedMimeTypes: exampleFileKind.allowedMimeTypes, + }); developerExamples.register({ appId: PLUGIN_ID, diff --git a/package.json b/package.json index ff3ed15dc46e9..a363b2c162e56 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ }, "dependencies": { "@appland/sql-parser": "^1.5.1", - "@babel/runtime": "^7.20.13", + "@babel/runtime": "^7.21.0", "@dnd-kit/core": "^3.1.1", "@dnd-kit/sortable": "^4.0.0", "@dnd-kit/utilities": "^2.0.0", @@ -96,7 +96,7 @@ "@elastic/datemath": "5.0.3", "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@8.6.0-canary.3", "@elastic/ems-client": "8.4.0", - "@elastic/eui": "75.1.0", + "@elastic/eui": "75.1.2", "@elastic/filesaver": "1.1.2", "@elastic/node-crypto": "1.2.1", "@elastic/numeral": "^2.5.1", @@ -134,6 +134,7 @@ "@kbn/alerting-fixture-plugin": "link:x-pack/test/functional_with_es_ssl/plugins/alerts", "@kbn/alerting-plugin": "link:x-pack/plugins/alerting", "@kbn/alerts": "link:packages/kbn-alerts", + "@kbn/alerts-as-data-utils": "link:packages/kbn-alerts-as-data-utils", "@kbn/alerts-restricted-fixtures-plugin": "link:x-pack/test/alerting_api_integration/common/plugins/alerts_restricted", "@kbn/alerts-ui-shared": "link:packages/kbn-alerts-ui-shared", "@kbn/analytics": "link:packages/kbn-analytics", @@ -181,6 +182,7 @@ "@kbn/config-schema": "link:packages/kbn-config-schema", "@kbn/console-plugin": "link:src/plugins/console", "@kbn/content-management-content-editor": "link:packages/content-management/content_editor", + "@kbn/content-management-examples-plugin": "link:examples/content_management_examples", "@kbn/content-management-plugin": "link:src/plugins/content_management", "@kbn/content-management-table-list": "link:packages/content-management/table_list", "@kbn/controls-example-plugin": "link:examples/controls_example", @@ -369,6 +371,7 @@ "@kbn/event-annotation-plugin": "link:src/plugins/event_annotation", "@kbn/event-log-fixture-plugin": "link:x-pack/test/plugin_api_integration/plugins/event_log", "@kbn/event-log-plugin": "link:x-pack/plugins/event_log", + "@kbn/expandable-flyout": "link:packages/kbn-expandable-flyout", "@kbn/exploratory-view-example-plugin": "link:x-pack/examples/exploratory_view_example", "@kbn/expression-error-plugin": "link:src/plugins/expression_error", "@kbn/expression-gauge-plugin": "link:src/plugins/chart_expressions/expression_gauge", @@ -933,26 +936,26 @@ }, "devDependencies": { "@apidevtools/swagger-parser": "^10.0.3", - "@babel/cli": "^7.20.7", - "@babel/core": "^7.20.12", + "@babel/cli": "^7.21.0", + "@babel/core": "^7.21.0", "@babel/eslint-parser": "^7.19.1", "@babel/eslint-plugin": "^7.19.1", - "@babel/generator": "^7.20.14", + "@babel/generator": "^7.21.1", "@babel/helper-plugin-utils": "^7.20.2", - "@babel/parser": "^7.20.15", + "@babel/parser": "^7.21.1", "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-proposal-export-namespace-from": "^7.18.9", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", "@babel/plugin-proposal-object-rest-spread": "^7.20.7", - "@babel/plugin-proposal-optional-chaining": "^7.20.7", + "@babel/plugin-proposal-optional-chaining": "^7.21.0", "@babel/plugin-proposal-private-methods": "^7.18.6", - "@babel/plugin-transform-runtime": "^7.19.6", + "@babel/plugin-transform-runtime": "^7.21.0", "@babel/preset-env": "^7.20.2", "@babel/preset-react": "^7.18.6", - "@babel/preset-typescript": "^7.18.6", - "@babel/register": "^7.18.9", - "@babel/traverse": "^7.20.13", - "@babel/types": "^7.20.7", + "@babel/preset-typescript": "^7.21.0", + "@babel/register": "^7.21.0", + "@babel/traverse": "^7.21.0", + "@babel/types": "^7.21.0", "@bazel/ibazel": "^0.16.2", "@bazel/typescript": "4.6.2", "@cypress/code-coverage": "^3.10.0", @@ -1311,7 +1314,7 @@ "cssnano": "^5.1.12", "cssnano-preset-default": "^5.2.12", "csstype": "^3.0.2", - "cypress": "^12.5.1", + "cypress": "^12.6.0", "cypress-axe": "^1.3.0", "cypress-file-upload": "^5.0.8", "cypress-multi-reporters": "^1.6.2", @@ -1430,7 +1433,7 @@ "resolve": "^1.22.0", "rxjs-marbles": "^7.0.1", "sass-loader": "^10.4.1", - "selenium-webdriver": "^4.8.0", + "selenium-webdriver": "^4.8.1", "simple-git": "^3.16.0", "sinon": "^7.4.2", "sort-package-json": "^1.53.1", @@ -1445,7 +1448,7 @@ "svgo": "^2.8.0", "tape": "^5.0.1", "tempy": "^0.3.0", - "terser": "^5.16.3", + "terser": "^5.16.4", "terser-webpack-plugin": "^4.2.3", "tough-cookie": "^4.1.2", "tree-kill": "^1.2.2", diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/header/__snapshots__/header.test.tsx.snap b/packages/core/chrome/core-chrome-browser-internal/src/ui/header/__snapshots__/header.test.tsx.snap index 34a68939dfb56..d86194ac7dbaf 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/header/__snapshots__/header.test.tsx.snap +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/header/__snapshots__/header.test.tsx.snap @@ -1,188 +1,208 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Header renders 1`] = ` -
    +Array [
    , +
    + -
    -
    -
    -
    -
    + class="euiHeaderSection euiHeaderSection--dontGrow euiHeaderSection--left" + > +
    +
    +
    +
    - + +
    +
    -
    -
    -
    - + +
    -
    - -
    + + test + + + +
    + class="euiHeaderSectionItem" + > +
    +
    -
    -
    +
    , +] `; diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/header/__snapshots__/screen_reader_a11y.test.tsx.snap b/packages/core/chrome/core-chrome-browser-internal/src/ui/header/__snapshots__/screen_reader_a11y.test.tsx.snap new file mode 100644 index 0000000000000..f10087a2eb2e0 --- /dev/null +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/header/__snapshots__/screen_reader_a11y.test.tsx.snap @@ -0,0 +1,317 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ScreenReaderRouteAnnouncements renders 1`] = ` + + + +
    + + + , + ], + }, + } + } + isStringTag={true} + serialized={ + Object { + "map": undefined, + "name": "hus3oj-euiScreenReaderOnly", + "next": undefined, + "styles": "; + // Take the element out of the layout + position: absolute; + // Keep it vertically inline + inset-block-start: auto; + // Chrome requires a left value, and Selenium (used by Kibana's FTR) requires an off-screen position for its .getVisibleText() to not register SR-only text + inset-inline-start: -10000px; + // The element must have a size (for some screen readers) + + inline-size: 1px; + block-size: 1px; + + // But reduce the visible size to nothing + clip: rect(0 0 0 0); + clip-path: inset(50%); + // And ensure no overflows occur + overflow: hidden; + // Chrome requires the negative margin to not cause overflows of parent containers + margin: -1px; +;label:euiScreenReaderOnly;;;;", + "toString": [Function], + } + } + /> +
    + +
    + + + +`; diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header.tsx index 66297bf9a9b86..227a94a208ca3 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header.tsx @@ -46,6 +46,7 @@ import { HeaderActionMenu } from './header_action_menu'; import { HeaderExtension } from './header_extension'; import { HeaderTopBanner } from './header_top_banner'; import { HeaderMenuButton } from './header_menu_button'; +import { ScreenReaderRouteAnnouncements } from './screen_reader_a11y'; export interface HeaderProps { kibanaVersion: string; @@ -108,6 +109,12 @@ export function Header({ return ( <> + +
    diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/header/screen_reader_a11y.test.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/header/screen_reader_a11y.test.tsx new file mode 100644 index 0000000000000..3dfc25c93c25a --- /dev/null +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/header/screen_reader_a11y.test.tsx @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { BehaviorSubject } from 'rxjs'; +import { mountWithIntl } from '@kbn/test-jest-helpers'; +import { ScreenReaderRouteAnnouncements } from './screen_reader_a11y'; +import { mount } from 'enzyme'; + +describe('ScreenReaderRouteAnnouncements', () => { + it('renders', () => { + const component = mountWithIntl( + + ); + expect(component).toMatchSnapshot(); + }); + + it('does not set the focusOnRegionOnTextChange for canvas or discover', () => { + const noFocusComponentCanvas = mount( + + ); + const noFocusComponentDiscover = mount( + + ); + + expect( + noFocusComponentCanvas + .debug() + .includes('') + ).toBeTruthy(); + + expect( + noFocusComponentDiscover + .debug() + .includes('') + ).toBeTruthy(); + }); + + it('sets the focusOnRegionOnTextChange to true for other apps', () => { + const noFocusComponent = mount( + + ); + + expect( + noFocusComponent.debug().includes('') + ).toBeTruthy(); + }); +}); diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/header/screen_reader_a11y.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/header/screen_reader_a11y.tsx new file mode 100644 index 0000000000000..811da52341417 --- /dev/null +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/header/screen_reader_a11y.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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { FC, useState, useEffect } from 'react'; +import useObservable from 'react-use/lib/useObservable'; +import { EuiScreenReaderLive } from '@elastic/eui'; + +import type { InternalApplicationStart } from '@kbn/core-application-browser-internal'; +import type { HeaderProps } from './header'; + +const DEFAULT_BRAND = 'Elastic'; // This may need to be DRYed out with https://github.com/elastic/kibana/blob/main/packages/core/rendering/core-rendering-server-internal/src/views/template.tsx#L34 +const SEPARATOR = ' - '; + +export const ScreenReaderRouteAnnouncements: FC<{ + breadcrumbs$: HeaderProps['breadcrumbs$']; + customBranding$: HeaderProps['customBranding$']; + appId$: InternalApplicationStart['currentAppId$']; +}> = ({ breadcrumbs$, customBranding$, appId$ }) => { + const [routeTitle, setRouteTitle] = useState(''); + const branding = useObservable(customBranding$)?.pageTitle || DEFAULT_BRAND; + const breadcrumbs = useObservable(breadcrumbs$, []); + + useEffect(() => { + if (breadcrumbs.length) { + const breadcrumbText: string[] = []; + + // Reverse the breadcrumb title order and ensure we only pick up valid strings + [...breadcrumbs].reverse().forEach((breadcrumb) => { + if (typeof breadcrumb.text === 'string') breadcrumbText.push(breadcrumb.text); + }); + breadcrumbText.push(branding); + + setRouteTitle(breadcrumbText.join(SEPARATOR)); + } else { + // Don't announce anything during loading states + setRouteTitle(''); + } + }, [breadcrumbs, branding]); + + // 1. Canvas dynamically updates breadcrumbs *and* page title/history on every name onChange, + // which leads to focus fighting if this is enabled + // 2. Discover has custom h1 focus behavior on route change, which should probably + // be removed in favor of this for a more consistent SR experience + const appId = useObservable(appId$); + const disableFocusForApps = ['canvas', 'discover']; + const focusRegionOnTextChange = !disableFocusForApps.includes(appId || ''); + + return ( + + {routeTitle} + + ); +}; diff --git a/packages/core/http/core-http-router-server-internal/src/response.test.ts b/packages/core/http/core-http-router-server-internal/src/response.test.ts new file mode 100644 index 0000000000000..84f22f076528c --- /dev/null +++ b/packages/core/http/core-http-router-server-internal/src/response.test.ts @@ -0,0 +1,108 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { fileResponseFactory } from './response'; + +describe('fileResponseFactory', () => { + describe('res.file', () => { + it('returns a kibana response with attachment', () => { + const body = Buffer.from('Attachment content'); + const result = fileResponseFactory.file({ + body, + filename: 'myfile.test', + fileContentSize: 30, + }); + expect(result.payload).toBe(body); + expect(result.status).toBe(200); + expect(result.options.headers).toMatchInlineSnapshot(` + Object { + "content-disposition": "attachment; filename=myfile.test", + "content-length": "30", + "content-type": "application/octet-stream", + "x-content-type-options": "nosniff", + } + `); + }); + + it('converts string body content to buffer in response', () => { + const body = 'I am a string'; + const result = fileResponseFactory.file({ body, filename: 'myfile.test' }); + expect(result.payload?.toString()).toBe(body); + }); + + it('doesnt pass utf-16 characters in filename into the content-disposition header', () => { + const isMultiByte = (str: string) => [...str].some((c) => (c.codePointAt(0) || 0) > 255); + const multuByteCharacters = '日本語ダッシュボード.pdf'; + + const result = fileResponseFactory.file({ + body: 'content', + filename: multuByteCharacters, + }); + const { headers } = result.options; + if (!headers) { + throw new Error('Missing headers'); + } + + const contentDispositionHeader = headers['content-disposition']; + if (typeof contentDispositionHeader !== 'string') { + throw new Error(`Expecting a string content-disposition header`); + } + + expect(typeof contentDispositionHeader).toBe('string'); + expect(isMultiByte(multuByteCharacters)).toBe(true); + expect(contentDispositionHeader).toMatchInlineSnapshot( + `"attachment; filename=%E6%97%A5%E6%9C%AC%E8%AA%9E%E3%83%80%E3%83%83%E3%82%B7%E3%83%A5%E3%83%9C%E3%83%BC%E3%83%89.pdf"` + ); + expect(isMultiByte(contentDispositionHeader)).toBe(false); + expect(decodeURIComponent(contentDispositionHeader)).toBe( + `attachment; filename=${multuByteCharacters}` + ); + }); + + it('accepts additional headers but doesnt override file headers', () => { + const extraHeaders = { 'content-language': 'en', 'x-test-header': 'ok' }; + const overrideHeaders = { + 'content-disposition': 'i will not be in the response', + 'content-length': 'i will not be in the response', + }; + const filename = 'myfile.test'; + const fileContent = 'content'; + const result = fileResponseFactory.file({ + body: fileContent, + filename, + headers: { ...extraHeaders, ...overrideHeaders }, + }); + expect(result.options.headers).toEqual( + expect.objectContaining({ + ...extraHeaders, + 'content-disposition': `attachment; filename=${filename}`, + 'content-length': `${fileContent.length}`, + }) + ); + }); + + describe('content-type', () => { + it('default mime type octet-stream', () => { + const result = fileResponseFactory.file({ body: 'content', filename: 'myfile.unknown' }); + expect(result.options.headers).toHaveProperty('content-type', 'application/octet-stream'); + }); + it('gets mime type from filename', () => { + const result = fileResponseFactory.file({ body: 'content', filename: 'myfile.mp4' }); + expect(result.options.headers).toHaveProperty('content-type', 'video/mp4'); + }); + it('gets accepts contentType override', () => { + const result = fileResponseFactory.file({ + body: 'content', + filename: 'myfile.mp4', + fileContentType: 'custom', + }); + expect(result.options.headers).toHaveProperty('content-type', 'custom'); + }); + }); + }); +}); diff --git a/packages/core/http/core-http-router-server-internal/src/response.ts b/packages/core/http/core-http-router-server-internal/src/response.ts index ce7a88fb5cbb9..1fc8d310233c7 100644 --- a/packages/core/http/core-http-router-server-internal/src/response.ts +++ b/packages/core/http/core-http-router-server-internal/src/response.ts @@ -5,7 +5,6 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ - import { Stream } from 'stream'; import type { IKibanaResponse, @@ -14,6 +13,7 @@ import type { HttpResponseOptions, RedirectResponseOptions, CustomHttpResponseOptions, + FileHttpResponseOptions, ErrorHttpResponseOptions, KibanaErrorResponseFactory, KibanaRedirectionResponseFactory, @@ -21,6 +21,7 @@ import type { KibanaResponseFactory, LifecycleResponseFactory, } from '@kbn/core-http-server'; +import mime from 'mime'; export function isKibanaResponse(response: Record): response is IKibanaResponse { return typeof response.status === 'number' && typeof response.options === 'object'; @@ -76,10 +77,51 @@ const errorResponseFactory: KibanaErrorResponseFactory = { }, }; +export const fileResponseFactory = { + file: (options: FileHttpResponseOptions) => { + const { + body, + bypassErrorFormat, + fileContentSize, + headers, + filename, + fileContentType, + bypassFileNameEncoding, + } = options; + const reponseFilename = bypassFileNameEncoding ? filename : encodeURIComponent(filename); + const responseBody = typeof body === 'string' ? Buffer.from(body) : body; + if (!responseBody) { + throw new Error(`options.body is expected to be set.`); + } + + const responseContentType = + fileContentType ?? mime.getType(filename) ?? 'application/octet-stream'; + const responseContentLength = + typeof fileContentSize === 'number' + ? fileContentSize + : Buffer.isBuffer(responseBody) + ? responseBody.length + : ''; + + return new KibanaResponse(200, responseBody, { + bypassErrorFormat, + headers: { + ...headers, + 'content-type': `${responseContentType}`, + 'content-length': `${responseContentLength}`, + 'content-disposition': `attachment; filename=${reponseFilename}`, + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options + 'x-content-type-options': 'nosniff', + }, + }); + }, +}; + export const kibanaResponseFactory: KibanaResponseFactory = { ...successResponseFactory, ...redirectionResponseFactory, ...errorResponseFactory, + ...fileResponseFactory, custom: ( options: CustomHttpResponseOptions ) => { diff --git a/packages/core/http/core-http-router-server-mocks/src/router.mock.ts b/packages/core/http/core-http-router-server-mocks/src/router.mock.ts index c6e2deb9b1473..1a3262fdf1f80 100644 --- a/packages/core/http/core-http-router-server-mocks/src/router.mock.ts +++ b/packages/core/http/core-http-router-server-mocks/src/router.mock.ts @@ -127,6 +127,7 @@ const createResponseFactoryMock = (): jest.Mocked => ({ notFound: jest.fn(), conflict: jest.fn(), customError: jest.fn(), + file: jest.fn(), }); export const mockRouter = { diff --git a/packages/core/http/core-http-server-internal/src/lifecycle/on_pre_response.ts b/packages/core/http/core-http-server-internal/src/lifecycle/on_pre_response.ts index 3a59d116a031d..057c7cc676c4e 100644 --- a/packages/core/http/core-http-server-internal/src/lifecycle/on_pre_response.ts +++ b/packages/core/http/core-http-server-internal/src/lifecycle/on_pre_response.ts @@ -91,7 +91,6 @@ export function adoptToHapiOnPreResponseFormat(fn: OnPreResponseHandler, log: Lo } } else if (preResponseResult.isRender(result)) { const overriddenResponse = responseToolkit.response(result.body).code(statusCode); - const originalHeaders = isBoom(response) ? response.output.headers : response.headers; setHeaders(overriddenResponse, originalHeaders as { [key: string]: string }); if (result.headers) { diff --git a/packages/core/http/core-http-server/index.ts b/packages/core/http/core-http-server/index.ts index d55ad7cf4ae69..fe08c6a34f2d4 100644 --- a/packages/core/http/core-http-server/index.ts +++ b/packages/core/http/core-http-server/index.ts @@ -69,6 +69,7 @@ export type { RequestHandlerContextBase, ResponseError, CustomHttpResponseOptions, + FileHttpResponseOptions, HttpResponseOptions, HttpResponsePayload, IKibanaResponse, diff --git a/packages/core/http/core-http-server/src/router/index.ts b/packages/core/http/core-http-server/src/router/index.ts index 60e35fbf6e279..c72d7386e867d 100644 --- a/packages/core/http/core-http-server/src/router/index.ts +++ b/packages/core/http/core-http-server/src/router/index.ts @@ -40,6 +40,7 @@ export type { RedirectResponseOptions, ResponseErrorAttributes, ErrorHttpResponseOptions, + FileHttpResponseOptions, } from './response'; export type { RouteConfigOptions, diff --git a/packages/core/http/core-http-server/src/router/response.ts b/packages/core/http/core-http-server/src/router/response.ts index db2ab80aa5324..41f7d84f6b5d1 100644 --- a/packages/core/http/core-http-server/src/router/response.ts +++ b/packages/core/http/core-http-server/src/router/response.ts @@ -69,6 +69,27 @@ export interface CustomHttpResponseOptions { + /** Attachment content to send to the client */ + body: T; + /** Attachment name, encoded and added to the headers to send to the client */ + filename: string; + /** Explicitly set the attachment content type. Tries to detect the type based on extension and defaults to application/octet-stream */ + fileContentType?: string | null; + /** Attachment content size in bytes, Tries to detect the content size from body */ + fileContentSize?: number; + /** HTTP Headers with additional information about response */ + headers?: ResponseHeaders; + /** Bypass the default error formatting */ + bypassErrorFormat?: boolean; + /** Bypass filename encoding, only set to true if the filename is already encoded */ + bypassFileNameEncoding?: boolean; +} + /** * HTTP response parameters for redirection response * @public diff --git a/packages/core/http/core-http-server/src/router/response_factory.ts b/packages/core/http/core-http-server/src/router/response_factory.ts index 00c71c945953f..9f9be08dc2cb7 100644 --- a/packages/core/http/core-http-server/src/router/response_factory.ts +++ b/packages/core/http/core-http-server/src/router/response_factory.ts @@ -13,6 +13,7 @@ import type { HttpResponsePayload, IKibanaResponse, RedirectResponseOptions, + FileHttpResponseOptions, ResponseError, ErrorHttpResponseOptions, } from './response'; @@ -196,6 +197,13 @@ export interface KibanaErrorResponseFactory { export type KibanaResponseFactory = KibanaSuccessResponseFactory & KibanaRedirectionResponseFactory & KibanaErrorResponseFactory & { + /** + * Creates a response with defined status code and payload. + * @param options - {@link FileHttpResponseOptions} configures HTTP response parameters. + */ + file( + options: FileHttpResponseOptions + ): IKibanaResponse; /** * Creates a response with defined status code and payload. * @param options - {@link CustomHttpResponseOptions} configures HTTP response parameters. diff --git a/packages/core/node/core-node-server-internal/src/node_config.test.ts b/packages/core/node/core-node-server-internal/src/node_config.test.ts new file mode 100644 index 0000000000000..cb5a1fb44cebd --- /dev/null +++ b/packages/core/node/core-node-server-internal/src/node_config.test.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { rolesConfig } from './node_config'; + +describe('rolesConfig', () => { + test('default', () => { + expect(rolesConfig.validate(undefined)).toEqual(['*']); + }); + test('empty', () => { + expect(() => rolesConfig.validate([])).toThrow(); + }); + test('"ui" and "background_tasks" roles are allowed and can be combined', () => { + expect(() => rolesConfig.validate(['ui', 'background_tasks'])).not.toThrow(); + expect(() => rolesConfig.validate(['ui'])).not.toThrow(); + expect(() => rolesConfig.validate(['background_tasks'])).not.toThrow(); + }); + test('exlcusive "*"', () => { + const wildcardError = `wildcard ("*") cannot be used with other roles or specified more than once`; + expect(() => rolesConfig.validate(['*'])).not.toThrow(); + + expect(() => rolesConfig.validate(['*', 'ui'])).toThrow(wildcardError); + expect(() => rolesConfig.validate(['*', '*'])).toThrow(wildcardError); + + expect(() => rolesConfig.validate(['*', 'unknown'])).toThrow(); + }); + test('exlcusive "migrator"', () => { + const migratorError = `"migrator" cannot be used with other roles or specified more than once`; + expect(() => rolesConfig.validate(['migrator'])).not.toThrow(); + + expect(() => rolesConfig.validate(['migrator', 'ui'])).toThrow(migratorError); + expect(() => rolesConfig.validate(['migrator', 'migrator'])).toThrow(migratorError); + + expect(() => rolesConfig.validate(['migrator', 'unknown'])).toThrow(); + }); +}); diff --git a/packages/core/node/core-node-server-internal/src/node_config.ts b/packages/core/node/core-node-server-internal/src/node_config.ts index e49d8a589296c..f6e8afa694fb7 100644 --- a/packages/core/node/core-node-server-internal/src/node_config.ts +++ b/packages/core/node/core-node-server-internal/src/node_config.ts @@ -6,33 +6,68 @@ * Side Public License, v 1. */ -import { schema } from '@kbn/config-schema'; +import { schema, TypeOf } from '@kbn/config-schema'; import type { ServiceConfigDescriptor } from '@kbn/core-base-server-internal'; /** @internal */ export const NODE_CONFIG_PATH = 'node' as const; +/** + * Wildchar is a special config option that implies all {@link NODE_DEFAULT_ROLES} roles. + * @internal + */ +export const NODE_WILDCARD_CHAR = '*' as const; +/** @internal */ +export const NODE_BACKGROUND_TASKS_ROLE = 'background_tasks' as const; +/** @internal */ +export const NODE_UI_ROLE = 'ui' as const; /** @internal */ -export const NODE_WILDCARD_CHAR = '*'; +export const NODE_MIGRATOR_ROLE = 'migrator' as const; /** @internal */ -export const NODE_ACCEPTED_ROLES = ['background_tasks', 'ui']; +export const NODE_DEFAULT_ROLES = [NODE_BACKGROUND_TASKS_ROLE, NODE_UI_ROLE] as const; +/** @internal */ +export const NODE_ALL_ROLES = [ + NODE_UI_ROLE, + NODE_MIGRATOR_ROLE, + NODE_BACKGROUND_TASKS_ROLE, +] as const; + +/** @internal */ +export const rolesConfig = schema.arrayOf( + schema.oneOf([ + schema.literal(NODE_BACKGROUND_TASKS_ROLE), + schema.literal(NODE_MIGRATOR_ROLE), + schema.literal(NODE_WILDCARD_CHAR), + schema.literal(NODE_UI_ROLE), + ]), + { + defaultValue: [NODE_WILDCARD_CHAR], + validate: (value) => { + if (value.length > 1) { + if (value.includes(NODE_WILDCARD_CHAR)) { + return `wildcard ("*") cannot be used with other roles or specified more than once`; + } + if (value.includes(NODE_MIGRATOR_ROLE)) { + return `"migrator" cannot be used with other roles or specified more than once`; + } + } + }, + minSize: 1, + } +); + +/** @internal */ +export type NodeRolesConfig = TypeOf; /** @internal */ export interface NodeConfigType { - roles: string[]; + roles: NodeRolesConfig; } const configSchema = schema.object({ - roles: schema.oneOf( - [ - schema.arrayOf(schema.oneOf([schema.literal('background_tasks'), schema.literal('ui')])), - schema.arrayOf(schema.literal(NODE_WILDCARD_CHAR), { minSize: 1, maxSize: 1 }), - ], - { - defaultValue: [NODE_WILDCARD_CHAR], - } - ), + roles: rolesConfig, }); +/** @internal */ export const nodeConfig: ServiceConfigDescriptor = { path: NODE_CONFIG_PATH, schema: configSchema, diff --git a/packages/core/node/core-node-server-internal/src/node_service.test.ts b/packages/core/node/core-node-server-internal/src/node_service.test.ts index f25737ded31bb..464e3fbae79a7 100644 --- a/packages/core/node/core-node-server-internal/src/node_service.test.ts +++ b/packages/core/node/core-node-server-internal/src/node_service.test.ts @@ -11,12 +11,13 @@ import { BehaviorSubject } from 'rxjs'; import type { CoreContext } from '@kbn/core-base-server-internal'; import { NodeService } from './node_service'; +import type { NodeRolesConfig } from './node_config'; import { configServiceMock } from '@kbn/config-mocks'; import { mockCoreContext } from '@kbn/core-base-server-mocks'; import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; -const getMockedConfigService = (nodeConfig: unknown) => { +const getMockedConfigService = (nodeConfig: { roles: NodeRolesConfig }) => { const configService = configServiceMock.create(); configService.atPath.mockImplementation((path) => { if (path === 'node') { @@ -51,6 +52,7 @@ describe('NodeService', () => { expect(roles.backgroundTasks).toBe(true); expect(roles.ui).toBe(true); + expect(roles.migrator).toBe(false); }); it('returns correct roles when node is configured to `background_tasks`', async () => { @@ -62,6 +64,7 @@ describe('NodeService', () => { expect(roles.backgroundTasks).toBe(true); expect(roles.ui).toBe(false); + expect(roles.migrator).toBe(false); }); it('returns correct roles when node is configured to `ui`', async () => { @@ -73,6 +76,7 @@ describe('NodeService', () => { expect(roles.backgroundTasks).toBe(false); expect(roles.ui).toBe(true); + expect(roles.migrator).toBe(false); }); it('returns correct roles when node is configured to both `background_tasks` and `ui`', async () => { @@ -84,6 +88,19 @@ describe('NodeService', () => { expect(roles.backgroundTasks).toBe(true); expect(roles.ui).toBe(true); + expect(roles.migrator).toBe(false); + }); + + it('returns correct roles when node is configured to `migrator`', async () => { + configService = getMockedConfigService({ roles: ['migrator'] }); + coreContext = mockCoreContext.create({ logger, configService }); + + service = new NodeService(coreContext); + const { roles } = await service.preboot({ loggingSystem: logger }); + + expect(roles.backgroundTasks).toBe(false); + expect(roles.ui).toBe(false); + expect(roles.migrator).toBe(true); }); it('logs the node roles', async () => { diff --git a/packages/core/node/core-node-server-internal/src/node_service.ts b/packages/core/node/core-node-server-internal/src/node_service.ts index b5c5c0a8b4c17..d84582d57e892 100644 --- a/packages/core/node/core-node-server-internal/src/node_service.ts +++ b/packages/core/node/core-node-server-internal/src/node_service.ts @@ -14,13 +14,15 @@ import type { ILoggingSystem } from '@kbn/core-logging-server-internal'; import type { NodeRoles } from '@kbn/core-node-server'; import type { Logger } from '@kbn/logging'; import { - NodeConfigType, - NODE_WILDCARD_CHAR, - NODE_ACCEPTED_ROLES, + type NodeConfigType, + type NodeRolesConfig, + NODE_ALL_ROLES, NODE_CONFIG_PATH, + NODE_WILDCARD_CHAR, + NODE_DEFAULT_ROLES, } from './node_config'; -const DEFAULT_ROLES = NODE_ACCEPTED_ROLES; +const DEFAULT_ROLES = [...NODE_DEFAULT_ROLES]; const containsWildcard = (roles: string[]) => roles.includes(NODE_WILDCARD_CHAR); /** @@ -66,8 +68,9 @@ export class NodeService { loggingSystem.setGlobalContext({ service: { node: { roles } } }); this.log.info(`Kibana process configured with roles: [${roles.join(', ')}]`); - this.roles = NODE_ACCEPTED_ROLES.reduce((acc, curr) => { - return { ...acc, [camelCase(curr)]: roles.includes(curr) }; + // We assume the combination of node roles has been validated and avoid doing additional checks here. + this.roles = NODE_ALL_ROLES.reduce((acc, curr) => { + return { ...acc, [camelCase(curr)]: (roles as string[]).includes(curr) }; }, {} as NodeRoles); return { @@ -86,7 +89,7 @@ export class NodeService { // nothing to do here yet } - private async getNodeRoles(): Promise { + private async getNodeRoles(): Promise { const { roles } = await firstValueFrom( this.configService.atPath(NODE_CONFIG_PATH) ); diff --git a/packages/core/node/core-node-server-mocks/src/node_service.mock.ts b/packages/core/node/core-node-server-mocks/src/node_service.mock.ts index ff354b663c231..2ab7f304e943a 100644 --- a/packages/core/node/core-node-server-mocks/src/node_service.mock.ts +++ b/packages/core/node/core-node-server-mocks/src/node_service.mock.ts @@ -18,6 +18,7 @@ const createInternalPrebootContractMock = () => { roles: { backgroundTasks: true, ui: true, + migrator: false, }, }; return prebootContract; @@ -27,15 +28,18 @@ const createInternalStartContractMock = ( { ui, backgroundTasks, + migrator, }: { ui: boolean; backgroundTasks: boolean; - } = { ui: true, backgroundTasks: true } + migrator: boolean; + } = { ui: true, backgroundTasks: true, migrator: false } ) => { const startContract: jest.Mocked = { roles: { backgroundTasks, ui, + migrator, }, }; return startContract; diff --git a/packages/core/node/core-node-server/src/types.ts b/packages/core/node/core-node-server/src/types.ts index 9b5a889b2ae6d..f2e594067b5ca 100644 --- a/packages/core/node/core-node-server/src/types.ts +++ b/packages/core/node/core-node-server/src/types.ts @@ -37,4 +37,9 @@ export interface NodeRoles { * to handle http traffic from the browser. */ ui: boolean; + /** + * Start Kibana with the specific purpose of completing the migrations phase then shutting down. + * @remark This role is special as it precludes the use of other roles. + */ + migrator: boolean; } diff --git a/packages/core/notifications/core-notifications-browser-internal/src/toasts/error_toast.tsx b/packages/core/notifications/core-notifications-browser-internal/src/toasts/error_toast.tsx index fb8b9305d817c..b1c21064d3c91 100644 --- a/packages/core/notifications/core-notifications-browser-internal/src/toasts/error_toast.tsx +++ b/packages/core/notifications/core-notifications-browser-internal/src/toasts/error_toast.tsx @@ -74,7 +74,7 @@ function showErrorDialog({ {title} - + {text && ( diff --git a/packages/core/notifications/core-notifications-browser-internal/src/toasts/toasts_api.tsx b/packages/core/notifications/core-notifications-browser-internal/src/toasts/toasts_api.tsx index 43e95b68ade20..2b2de9f945c74 100644 --- a/packages/core/notifications/core-notifications-browser-internal/src/toasts/toasts_api.tsx +++ b/packages/core/notifications/core-notifications-browser-internal/src/toasts/toasts_api.tsx @@ -150,7 +150,7 @@ export class ToastsApi implements IToasts { public addDanger(toastOrTitle: ToastInput, options?: ToastOptions) { return this.add({ color: 'danger', - iconType: 'alert', + iconType: 'error', toastLifeTimeMs: this.uiSettings.get('notifications:lifetime:warning'), ...normalizeToast(toastOrTitle), ...options, @@ -168,7 +168,7 @@ export class ToastsApi implements IToasts { const message = options.toastMessage || error.message; return this.add({ color: 'danger', - iconType: 'alert', + iconType: 'error', toastLifeTimeMs: this.uiSettings.get('notifications:lifetime:error'), text: mountReactNode( { roles: { backgroundTasks: true, ui: true, + migrator: false, }, }; diff --git a/packages/core/plugins/core-plugins-server-internal/src/plugin_context.test.ts b/packages/core/plugins/core-plugins-server-internal/src/plugin_context.test.ts index bba736aa3d31f..8d2c723aa5cb8 100644 --- a/packages/core/plugins/core-plugins-server-internal/src/plugin_context.test.ts +++ b/packages/core/plugins/core-plugins-server-internal/src/plugin_context.test.ts @@ -195,7 +195,7 @@ describe('createPluginInitializerContext', () => { opaqueId, manifest: createPluginManifest(), instanceInfo, - nodeInfo: { roles: { backgroundTasks: false, ui: true } }, + nodeInfo: { roles: { backgroundTasks: false, ui: true, migrator: false } }, }); expect(pluginInitializerContext.node.roles.backgroundTasks).toBe(false); expect(pluginInitializerContext.node.roles.ui).toBe(true); diff --git a/packages/core/plugins/core-plugins-server-internal/src/plugin_context.ts b/packages/core/plugins/core-plugins-server-internal/src/plugin_context.ts index 62d2f2de6383e..595473e35ea59 100644 --- a/packages/core/plugins/core-plugins-server-internal/src/plugin_context.ts +++ b/packages/core/plugins/core-plugins-server-internal/src/plugin_context.ts @@ -78,6 +78,7 @@ export function createPluginInitializerContext({ roles: { backgroundTasks: nodeInfo.roles.backgroundTasks, ui: nodeInfo.roles.ui, + migrator: nodeInfo.roles.migrator, }, }, diff --git a/packages/core/plugins/core-plugins-server-internal/src/plugins_service.test.ts b/packages/core/plugins/core-plugins-server-internal/src/plugins_service.test.ts index 62742499471cc..b8f8b5d2b9be5 100644 --- a/packages/core/plugins/core-plugins-server-internal/src/plugins_service.test.ts +++ b/packages/core/plugins/core-plugins-server-internal/src/plugins_service.test.ts @@ -728,7 +728,7 @@ describe('PluginsService', () => { }, coreContext: { coreId, env, logger, configService }, instanceInfo: { uuid: 'uuid' }, - nodeInfo: { roles: { backgroundTasks: true, ui: true } }, + nodeInfo: { roles: { backgroundTasks: true, ui: true, migrator: false } }, }); const logs = loggingSystemMock.collect(logger); diff --git a/packages/core/root/core-root-server-internal/index.ts b/packages/core/root/core-root-server-internal/index.ts index d6150b7aae8fc..5c1de0015e861 100644 --- a/packages/core/root/core-root-server-internal/index.ts +++ b/packages/core/root/core-root-server-internal/index.ts @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export { Server, Root, bootstrap } from './src'; +export { Server, registerServiceConfig, Root, bootstrap } from './src'; diff --git a/packages/core/root/core-root-server-internal/src/index.ts b/packages/core/root/core-root-server-internal/src/index.ts index 7573b34a28a6f..4d23b995211e6 100644 --- a/packages/core/root/core-root-server-internal/src/index.ts +++ b/packages/core/root/core-root-server-internal/src/index.ts @@ -7,5 +7,6 @@ */ export { Server } from './server'; +export { registerServiceConfig } from './register_service_config'; export { bootstrap } from './bootstrap'; export { Root } from './root'; diff --git a/packages/core/root/core-root-server-internal/src/register_service_config.ts b/packages/core/root/core-root-server-internal/src/register_service_config.ts new file mode 100644 index 0000000000000..a22ea56f25ee9 --- /dev/null +++ b/packages/core/root/core-root-server-internal/src/register_service_config.ts @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { config as pathConfig } from '@kbn/utils'; +import { ConfigService } from '@kbn/config'; +import type { ServiceConfigDescriptor } from '@kbn/core-base-server-internal'; +import { config as loggingConfig } from '@kbn/core-logging-server-internal'; +import { coreDeprecationProvider } from '@kbn/core-config-server-internal'; +import { nodeConfig } from '@kbn/core-node-server-internal'; +import { pidConfig } from '@kbn/core-environment-server-internal'; +import { executionContextConfig } from '@kbn/core-execution-context-server-internal'; +import { config as httpConfig, cspConfig, externalUrlConfig } from '@kbn/core-http-server-internal'; +import { config as elasticsearchConfig } from '@kbn/core-elasticsearch-server-internal'; +import { opsConfig } from '@kbn/core-metrics-server-internal'; +import { + savedObjectsConfig, + savedObjectsMigrationConfig, +} from '@kbn/core-saved-objects-base-server-internal'; +import { config as i18nConfig } from '@kbn/core-i18n-server-internal'; +import { config as deprecationConfig } from '@kbn/core-deprecations-server-internal'; +import { statusConfig } from '@kbn/core-status-server-internal'; +import { uiSettingsConfig } from '@kbn/core-ui-settings-server-internal'; + +import { config as pluginsConfig } from '@kbn/core-plugins-server-internal'; +import { elasticApmConfig } from './root/elastic_config'; + +const rootConfigPath = ''; + +export function registerServiceConfig(configService: ConfigService) { + const configDescriptors: Array> = [ + cspConfig, + deprecationConfig, + elasticsearchConfig, + elasticApmConfig, + executionContextConfig, + externalUrlConfig, + httpConfig, + i18nConfig, + loggingConfig, + nodeConfig, + opsConfig, + pathConfig, + pidConfig, + pluginsConfig, + savedObjectsConfig, + savedObjectsMigrationConfig, + statusConfig, + uiSettingsConfig, + ]; + + configService.addDeprecationProvider(rootConfigPath, coreDeprecationProvider); + for (const descriptor of configDescriptors) { + if (descriptor.deprecations) { + configService.addDeprecationProvider(descriptor.path, descriptor.deprecations); + } + configService.setSchema(descriptor.path, descriptor.schema); + } +} diff --git a/packages/core/root/core-root-server-internal/src/server.ts b/packages/core/root/core-root-server-internal/src/server.ts index d7580f19526d8..8c8d636d795e9 100644 --- a/packages/core/root/core-root-server-internal/src/server.ts +++ b/packages/core/root/core-root-server-internal/src/server.ts @@ -7,57 +7,30 @@ */ import apm from 'elastic-apm-node'; -import { config as pathConfig } from '@kbn/utils'; import { reportPerformanceMetricEvent } from '@kbn/ebt-tools'; import type { Logger, LoggerFactory } from '@kbn/logging'; import { ConfigService, Env, RawConfigurationProvider } from '@kbn/config'; -import type { ServiceConfigDescriptor } from '@kbn/core-base-server-internal'; import { DocLinksService } from '@kbn/core-doc-links-server-internal'; -import { - LoggingService, - ILoggingSystem, - config as loggingConfig, -} from '@kbn/core-logging-server-internal'; -import { - coreDeprecationProvider, - ensureValidConfiguration, -} from '@kbn/core-config-server-internal'; -import { NodeService, nodeConfig } from '@kbn/core-node-server-internal'; +import { LoggingService, ILoggingSystem } from '@kbn/core-logging-server-internal'; +import { ensureValidConfiguration } from '@kbn/core-config-server-internal'; +import { NodeService } from '@kbn/core-node-server-internal'; import { AnalyticsService } from '@kbn/core-analytics-server-internal'; import type { AnalyticsServiceSetup, AnalyticsServiceStart } from '@kbn/core-analytics-server'; -import { EnvironmentService, pidConfig } from '@kbn/core-environment-server-internal'; -import { - ExecutionContextService, - executionContextConfig, -} from '@kbn/core-execution-context-server-internal'; +import { EnvironmentService } from '@kbn/core-environment-server-internal'; +import { ExecutionContextService } from '@kbn/core-execution-context-server-internal'; import { PrebootService } from '@kbn/core-preboot-server-internal'; import { ContextService } from '@kbn/core-http-context-server-internal'; -import { - HttpService, - config as httpConfig, - cspConfig, - externalUrlConfig, -} from '@kbn/core-http-server-internal'; -import { - ElasticsearchService, - config as elasticsearchConfig, -} from '@kbn/core-elasticsearch-server-internal'; -import { MetricsService, opsConfig } from '@kbn/core-metrics-server-internal'; +import { HttpService } from '@kbn/core-http-server-internal'; +import { ElasticsearchService } from '@kbn/core-elasticsearch-server-internal'; +import { MetricsService } from '@kbn/core-metrics-server-internal'; import { CapabilitiesService } from '@kbn/core-capabilities-server-internal'; import type { SavedObjectsServiceStart } from '@kbn/core-saved-objects-server'; -import { - savedObjectsConfig, - savedObjectsMigrationConfig, -} from '@kbn/core-saved-objects-base-server-internal'; import { SavedObjectsService } from '@kbn/core-saved-objects-server-internal'; -import { I18nService, config as i18nConfig } from '@kbn/core-i18n-server-internal'; -import { - DeprecationsService, - config as deprecationConfig, -} from '@kbn/core-deprecations-server-internal'; +import { I18nService } from '@kbn/core-i18n-server-internal'; +import { DeprecationsService } from '@kbn/core-deprecations-server-internal'; import { CoreUsageDataService } from '@kbn/core-usage-data-server-internal'; -import { StatusService, statusConfig } from '@kbn/core-status-server-internal'; -import { UiSettingsService, uiSettingsConfig } from '@kbn/core-ui-settings-server-internal'; +import { StatusService } from '@kbn/core-status-server-internal'; +import { UiSettingsService } from '@kbn/core-ui-settings-server-internal'; import { CustomBrandingService } from '@kbn/core-custom-branding-server-internal'; import { CoreRouteHandlerContext, @@ -75,16 +48,11 @@ import type { InternalCoreSetup, InternalCoreStart, } from '@kbn/core-lifecycle-server-internal'; -import { - DiscoveredPlugins, - PluginsService, - config as pluginsConfig, -} from '@kbn/core-plugins-server-internal'; +import { DiscoveredPlugins, PluginsService } from '@kbn/core-plugins-server-internal'; import { CoreAppsService } from '@kbn/core-apps-server-internal'; -import { elasticApmConfig } from './root/elastic_config'; +import { registerServiceConfig } from './register_service_config'; const coreId = Symbol('core'); -const rootConfigPath = ''; const KIBANA_STARTED_EVENT = 'kibana_started'; /** @internal */ @@ -465,34 +433,7 @@ export class Server { } public setupCoreConfig() { - const configDescriptors: Array> = [ - cspConfig, - deprecationConfig, - elasticsearchConfig, - elasticApmConfig, - executionContextConfig, - externalUrlConfig, - httpConfig, - i18nConfig, - loggingConfig, - nodeConfig, - opsConfig, - pathConfig, - pidConfig, - pluginsConfig, - savedObjectsConfig, - savedObjectsMigrationConfig, - statusConfig, - uiSettingsConfig, - ]; - - this.configService.addDeprecationProvider(rootConfigPath, coreDeprecationProvider); - for (const descriptor of configDescriptors) { - if (descriptor.deprecations) { - this.configService.addDeprecationProvider(descriptor.path, descriptor.deprecations); - } - this.configService.setSchema(descriptor.path, descriptor.schema); - } + registerServiceConfig(this.configService); } /** diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/saved_objects_config.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/saved_objects_config.ts index 22980bd8e88e7..c4d48d66a6a06 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/saved_objects_config.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/saved_objects_config.ts @@ -11,6 +11,9 @@ import { schema, TypeOf } from '@kbn/config-schema'; import type { ServiceConfigDescriptor } from '@kbn/core-base-server-internal'; const migrationSchema = schema.object({ + algorithm: schema.oneOf([schema.literal('v2'), schema.literal('zdt')], { + defaultValue: 'v2', + }), batchSize: schema.number({ defaultValue: 1_000 }), maxBatchSizeBytes: schema.byteSize({ defaultValue: '100mb' }), // 100mb is the default http.max_content_length Elasticsearch config value discardUnknownObjects: schema.maybe( diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/index.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/index.ts index 21fbd6f8b5329..9a4a728184388 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/index.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/index.ts @@ -6,10 +6,14 @@ * Side Public License, v 1. */ -export { DocumentMigrator, KibanaMigrator, buildActiveMappings, mergeTypes } from './src'; +export { DocumentMigrator, KibanaMigrator, buildActiveMappings, buildTypesMappings } from './src'; export type { KibanaMigratorOptions } from './src'; export { getAggregatedTypesDocuments } from './src/actions/check_for_unknown_docs'; -export { addExcludedTypesToBoolQuery } from './src/model/helpers'; +export { + addExcludedTypesToBoolQuery, + createBulkIndexOperationTuple, + createBulkDeleteOperationBody, +} from './src/model/helpers'; // these are only used for integration tests export { diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/README.md b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/README.md index 8655c3e9c2222..6ae7f7fb09c06 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/README.md +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/README.md @@ -369,9 +369,9 @@ completed this step: - temp index has a write block - temp index is not found ### New control state -1. If `currentBatch` is the last batch in `transformedDocBatches` +1. If `currentBatch` is the last batch in `bulkOperationBatches` → `REINDEX_SOURCE_TO_TEMP_READ` -2. If there are more batches left in `transformedDocBatches` +2. If there are more batches left in `bulkOperationBatches` → `REINDEX_SOURCE_TO_TEMP_INDEX_BULK` ## REINDEX_SOURCE_TO_TEMP_CLOSE_PIT diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/__snapshots__/migrations_state_action_machine.test.ts.snap b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/__snapshots__/migrations_state_action_machine.test.ts.snap index 6973b0b8a7081..cd2b218d58dd4 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/__snapshots__/migrations_state_action_machine.test.ts.snap +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/__snapshots__/migrations_state_action_machine.test.ts.snap @@ -18,6 +18,9 @@ Object { "duration": 0, "state": Object { "batchSize": 1000, + "bulkOperationBatches": Array [ + Array [], + ], "controlState": "LEGACY_REINDEX", "currentAlias": ".my-so-index", "discardCorruptObjects": false, @@ -127,19 +130,8 @@ Object { }, }, Object { - "bool": Object { - "must": Array [ - Object { - "match": Object { - "type": "search-session", - }, - }, - Object { - "match": Object { - "search-session.persisted": false, - }, - }, - ], + "term": Object { + "type": "upgrade-assistant-telemetry", }, }, ], @@ -190,7 +182,6 @@ Object { }, }, }, - "transformedDocBatches": Array [], "versionAlias": ".my-so-index_7.11.0", "versionIndex": ".my-so-index_7.11.0_001", "waitForMigrationCompletion": false, @@ -214,6 +205,9 @@ Object { "duration": 0, "state": Object { "batchSize": 1000, + "bulkOperationBatches": Array [ + Array [], + ], "controlState": "LEGACY_DELETE", "currentAlias": ".my-so-index", "discardCorruptObjects": false, @@ -323,19 +317,8 @@ Object { }, }, Object { - "bool": Object { - "must": Array [ - Object { - "match": Object { - "type": "search-session", - }, - }, - Object { - "match": Object { - "search-session.persisted": false, - }, - }, - ], + "term": Object { + "type": "upgrade-assistant-telemetry", }, }, ], @@ -390,7 +373,6 @@ Object { }, }, }, - "transformedDocBatches": Array [], "versionAlias": ".my-so-index_7.11.0", "versionIndex": ".my-so-index_7.11.0_001", "waitForMigrationCompletion": false, @@ -414,6 +396,9 @@ Object { "duration": 0, "state": Object { "batchSize": 1000, + "bulkOperationBatches": Array [ + Array [], + ], "controlState": "LEGACY_DELETE", "currentAlias": ".my-so-index", "discardCorruptObjects": false, @@ -523,19 +508,8 @@ Object { }, }, Object { - "bool": Object { - "must": Array [ - Object { - "match": Object { - "type": "search-session", - }, - }, - Object { - "match": Object { - "search-session.persisted": false, - }, - }, - ], + "term": Object { + "type": "upgrade-assistant-telemetry", }, }, ], @@ -594,7 +568,6 @@ Object { }, }, }, - "transformedDocBatches": Array [], "versionAlias": ".my-so-index_7.11.0", "versionIndex": ".my-so-index_7.11.0_001", "waitForMigrationCompletion": false, @@ -618,6 +591,9 @@ Object { "duration": 0, "state": Object { "batchSize": 1000, + "bulkOperationBatches": Array [ + Array [], + ], "controlState": "DONE", "currentAlias": ".my-so-index", "discardCorruptObjects": false, @@ -727,19 +703,8 @@ Object { }, }, Object { - "bool": Object { - "must": Array [ - Object { - "match": Object { - "type": "search-session", - }, - }, - Object { - "match": Object { - "search-session.persisted": false, - }, - }, - ], + "term": Object { + "type": "upgrade-assistant-telemetry", }, }, ], @@ -802,7 +767,6 @@ Object { }, }, }, - "transformedDocBatches": Array [], "versionAlias": ".my-so-index_7.11.0", "versionIndex": ".my-so-index_7.11.0_001", "waitForMigrationCompletion": false, @@ -864,6 +828,15 @@ Object { "duration": 0, "state": Object { "batchSize": 1000, + "bulkOperationBatches": Array [ + Array [ + Object { + "index": Object { + "_id": "1234", + }, + }, + ], + ], "controlState": "LEGACY_DELETE", "currentAlias": ".my-so-index", "discardCorruptObjects": false, @@ -973,19 +946,8 @@ Object { }, }, Object { - "bool": Object { - "must": Array [ - Object { - "match": Object { - "type": "search-session", - }, - }, - Object { - "match": Object { - "search-session.persisted": false, - }, - }, - ], + "term": Object { + "type": "upgrade-assistant-telemetry", }, }, ], @@ -1041,13 +1003,6 @@ Object { }, }, }, - "transformedDocBatches": Array [ - Array [ - Object { - "_id": "1234", - }, - ], - ], "versionAlias": ".my-so-index_7.11.0", "versionIndex": ".my-so-index_7.11.0_001", "waitForMigrationCompletion": false, @@ -1071,6 +1026,15 @@ Object { "duration": 0, "state": Object { "batchSize": 1000, + "bulkOperationBatches": Array [ + Array [ + Object { + "index": Object { + "_id": "1234", + }, + }, + ], + ], "controlState": "FATAL", "currentAlias": ".my-so-index", "discardCorruptObjects": false, @@ -1180,19 +1144,8 @@ Object { }, }, Object { - "bool": Object { - "must": Array [ - Object { - "match": Object { - "type": "search-session", - }, - }, - Object { - "match": Object { - "search-session.persisted": false, - }, - }, - ], + "term": Object { + "type": "upgrade-assistant-telemetry", }, }, ], @@ -1252,13 +1205,6 @@ Object { }, }, }, - "transformedDocBatches": Array [ - Array [ - Object { - "_id": "1234", - }, - ], - ], "versionAlias": ".my-so-index_7.11.0", "versionIndex": ".my-so-index_7.11.0_001", "waitForMigrationCompletion": false, diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/bulk_overwrite_transformed_documents.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/bulk_overwrite_transformed_documents.test.ts index 06b5dd762cffc..ac1daf3c8761f 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/bulk_overwrite_transformed_documents.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/bulk_overwrite_transformed_documents.test.ts @@ -40,7 +40,7 @@ describe('bulkOverwriteTransformedDocuments', () => { const task = bulkOverwriteTransformedDocuments({ client, index: 'new_index', - transformedDocs: [], + operations: [], refresh: 'wait_for', }); @@ -74,7 +74,7 @@ describe('bulkOverwriteTransformedDocuments', () => { const task = bulkOverwriteTransformedDocuments({ client, index: 'new_index', - transformedDocs: [], + operations: [], refresh: 'wait_for', }); @@ -99,7 +99,7 @@ describe('bulkOverwriteTransformedDocuments', () => { const task = bulkOverwriteTransformedDocuments({ client, index: 'new_index', - transformedDocs: [], + operations: [], refresh: 'wait_for', }); try { @@ -140,7 +140,7 @@ describe('bulkOverwriteTransformedDocuments', () => { const task = bulkOverwriteTransformedDocuments({ client, index: 'new_index', - transformedDocs: [], + operations: [], refresh: 'wait_for', }); @@ -193,7 +193,7 @@ describe('bulkOverwriteTransformedDocuments', () => { const task = bulkOverwriteTransformedDocuments({ client, index: 'new_index', - transformedDocs: [], + operations: [], refresh: 'wait_for', }); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/bulk_overwrite_transformed_documents.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/bulk_overwrite_transformed_documents.ts index 7a6e8b2d9a5b5..716683c1938fb 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/bulk_overwrite_transformed_documents.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/bulk_overwrite_transformed_documents.ts @@ -11,7 +11,6 @@ import * as TaskEither from 'fp-ts/lib/TaskEither'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { errors as esErrors } from '@elastic/elasticsearch'; import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; -import type { SavedObjectsRawDoc } from '@kbn/core-saved-objects-server'; import { catchRetryableEsClientErrors, type RetryableEsClientError, @@ -19,33 +18,13 @@ import { import { isWriteBlockException, isIndexNotFoundException } from './es_errors'; import { WAIT_FOR_ALL_SHARDS_TO_BE_ACTIVE } from './constants'; import type { TargetIndexHadWriteBlock, RequestEntityTooLargeException, IndexNotFound } from '.'; - -/** - * Given a document and index, creates a valid body for the Bulk API. - */ -export const createBulkOperationBody = (doc: SavedObjectsRawDoc, index: string) => { - return [ - { - index: { - _index: index, - _id: doc._id, - // overwrite existing documents - op_type: 'index', - // use optimistic concurrency control to ensure that outdated - // documents are only overwritten once with the latest version - if_seq_no: doc._seq_no, - if_primary_term: doc._primary_term, - }, - }, - doc._source, - ]; -}; +import type { BulkOperation } from '../model/create_batches'; /** @internal */ export interface BulkOverwriteTransformedDocumentsParams { client: ElasticsearchClient; index: string; - transformedDocs: SavedObjectsRawDoc[]; + operations: BulkOperation[]; refresh?: estypes.Refresh; } @@ -57,7 +36,7 @@ export const bulkOverwriteTransformedDocuments = ({ client, index, - transformedDocs, + operations, refresh = false, }: BulkOverwriteTransformedDocumentsParams): TaskEither.TaskEither< | RetryableEsClientError @@ -67,10 +46,6 @@ export const bulkOverwriteTransformedDocuments = 'bulk_index_succeeded' > => () => { - const body = transformedDocs.flatMap((doc) => { - return createBulkOperationBody(doc, index); - }); - return client .bulk({ // Because we only add aliases in the MARK_VERSION_INDEX_READY step we @@ -80,11 +55,13 @@ export const bulkOverwriteTransformedDocuments = // mappings. Such tampering could lead to many other problems and is // probably unlikely so for now we'll accept this risk and wait till // system indices puts in place a hard control. + index, require_alias: false, wait_for_active_shards: WAIT_FOR_ALL_SHARDS_TO_BE_ACTIVE, refresh, filter_path: ['items.*.error'], - body, + // we need to unwrap the existing BulkIndexOperationTuple's + operations: operations.flat(), }) .then((res) => { // Filter out version_conflict_engine_exception since these just mean diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/calculate_exclude_filters.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/calculate_exclude_filters.test.ts index 95a9a33831d09..da3c11686ec93 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/calculate_exclude_filters.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/calculate_exclude_filters.test.ts @@ -28,7 +28,7 @@ describe('calculateExcludeFilters', () => { expect(hook2).toHaveBeenCalledWith({ readonlyEsClient: { search: expect.any(Function) } }); expect(Either.isRight(result)).toBe(true); expect((result as Either.Right).right).toEqual({ - mustNotClauses: [ + filterClauses: [ { bool: { must: { term: { fieldA: '123' } } } }, { bool: { must: { term: { fieldB: 'abc' } } } }, ], @@ -49,7 +49,7 @@ describe('calculateExcludeFilters', () => { expect(Either.isRight(result)).toBe(true); expect((result as Either.Right).right).toEqual({ - mustNotClauses: [{ bool: { must: { term: { fieldB: 'abc' } } } }], + filterClauses: [{ bool: { must: { term: { fieldB: 'abc' } } } }], errorsByType: { type1: error }, }); }); @@ -91,7 +91,7 @@ describe('calculateExcludeFilters', () => { expect(Either.isRight(result)).toBe(true); expect((result as Either.Right).right).toEqual({ - mustNotClauses: [{ bool: { must: { term: { fieldB: 'abc' } } } }], + filterClauses: [{ bool: { must: { term: { fieldB: 'abc' } } } }], errorsByType: expect.any(Object), }); expect((result as Either.Right).right.errorsByType.type1.toString()).toMatchInlineSnapshot( diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/calculate_exclude_filters.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/calculate_exclude_filters.ts index d0cf8f85fc497..30cc39d0a8fba 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/calculate_exclude_filters.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/calculate_exclude_filters.ts @@ -23,7 +23,7 @@ export interface CalculateExcludeFiltersParams { export interface CalculatedExcludeFilter { /** Array with all the clauses that must be bool.must_not'ed */ - mustNotClauses: QueryDslQueryContainer[]; + filterClauses: QueryDslQueryContainer[]; /** Any errors that were encountered during filter calculation, keyed by the type name */ errorsByType: Record; } @@ -91,17 +91,17 @@ export const calculateExcludeFilters = } const errorsByType: Array<[string, Error]> = []; - const mustNotClauses: QueryDslQueryContainer[] = []; + const filterClauses: QueryDslQueryContainer[] = []; // Loop through all results and collect successes and errors results.forEach((r) => Either.isRight(r) - ? mustNotClauses.push(r.right) + ? filterClauses.push(r.right) : Either.isLeft(r) && errorsByType.push([r.left.soType, r.left.error as Error]) ); return Either.right({ - mustNotClauses, + filterClauses, errorsByType: Object.fromEntries(errorsByType), }); }); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/check_for_unknown_docs.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/check_for_unknown_docs.ts index 74dc39bc6fcd4..e483a16c270ff 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/check_for_unknown_docs.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/check_for_unknown_docs.ts @@ -116,7 +116,7 @@ export const checkForUnknownDocs = RetryableEsClientError, UnknownDocsFound | {} > => - async () => { + () => { const excludeQuery = addExcludedTypesToBoolQuery(knownTypes, excludeOnUpgradeQuery.bool); return getAggregatedTypesDocuments(client, indexName, excludeQuery) .then((unknownDocs) => { diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/cleanup_unknown_and_excluded.mocks.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/cleanup_unknown_and_excluded.mocks.ts new file mode 100644 index 0000000000000..1b0e0a49e5062 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/cleanup_unknown_and_excluded.mocks.ts @@ -0,0 +1,137 @@ +/* + * Copyright 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 { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; + +export const emptyResponseClientMock = elasticsearchClientMock.createInternalClient( + Promise.resolve({ + took: 0, + timed_out: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 0, + relation: 'eq', + }, + max_score: null, + hits: [], + }, + }) +); + +export const initialExcludeOnUpgradeQueryMock = { + bool: { + must_not: [ + { + term: { + type: 'apm-services-telemetry', + }, + }, + { + term: { + type: 'application_usage_transactional', + }, + }, + { + term: { + type: 'background-session', + }, + }, + { + term: { + type: 'cases-sub-case', + }, + }, + { + term: { + type: 'csp_rule', + }, + }, + { + term: { + type: 'file-upload-telemetry', + }, + }, + { + term: { + type: 'fleet-agent-actions', + }, + }, + { + term: { + type: 'fleet-agent-events', + }, + }, + { + term: { + type: 'fleet-agents', + }, + }, + { + term: { + type: 'fleet-enrollment-api-keys', + }, + }, + { + term: { + type: 'guided-setup-state', + }, + }, + { + term: { + type: 'maps-telemetry', + }, + }, + { + term: { + type: 'ml-telemetry', + }, + }, + { + term: { + type: 'osquery-usage-metric', + }, + }, + { + term: { + type: 'server', + }, + }, + { + term: { + type: 'siem-detection-engine-rule-execution-info', + }, + }, + { + term: { + type: 'siem-detection-engine-rule-status', + }, + }, + { + term: { + type: 'timelion-sheet', + }, + }, + { + term: { + type: 'tsvb-validation-telemetry', + }, + }, + { + term: { + type: 'ui-counter', + }, + }, + ], + }, +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/cleanup_unknown_and_excluded.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/cleanup_unknown_and_excluded.test.ts new file mode 100644 index 0000000000000..af8fb8696d728 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/cleanup_unknown_and_excluded.test.ts @@ -0,0 +1,276 @@ +/* + * Copyright 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 * as Either from 'fp-ts/lib/Either'; +import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; +import { checkForUnknownDocs, type DocumentIdAndType } from './check_for_unknown_docs'; +import { cleanupUnknownAndExcluded } from './cleanup_unknown_and_excluded'; +import { calculateExcludeFilters } from './calculate_exclude_filters'; +import { deleteByQuery } from './delete_by_query'; +import { + emptyResponseClientMock, + initialExcludeOnUpgradeQueryMock, +} from './cleanup_unknown_and_excluded.mocks'; + +jest.mock('./check_for_unknown_docs'); +jest.mock('./calculate_exclude_filters'); +jest.mock('./delete_by_query'); + +const mockCheckForUnknownDocs = checkForUnknownDocs as jest.MockedFunction< + typeof checkForUnknownDocs +>; + +const mockCalculateExcludeFilters = calculateExcludeFilters as jest.MockedFunction< + typeof calculateExcludeFilters +>; + +const mockDeleteByQuery = deleteByQuery as jest.MockedFunction; + +describe('cleanupUnknownAndExcluded', () => { + const unknownDocs: DocumentIdAndType[] = [ + { id: 'dashboard:12345', type: 'dashboard' }, + { id: 'dashboard:67890', type: 'dashboard' }, + ]; + + const excludeFromUpgradeFilterHooks = { + 'search-session': async () => { + return { + bool: { + must: [ + { term: { type: 'search-session' } }, + { match: { 'search-session.persisted': false } }, + ], + }, + }; + }, + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('calls `Actions.checkForUnknownDocs()` with the correct params', async () => { + mockCheckForUnknownDocs.mockReturnValueOnce(async () => Either.right({})); + mockCalculateExcludeFilters.mockReturnValueOnce(async () => + Either.right({ + filterClauses: [], + errorsByType: {}, + }) + ); + mockDeleteByQuery.mockReturnValueOnce(async () => + Either.right({ + taskId: '1234', + }) + ); + + const task = cleanupUnknownAndExcluded({ + client: emptyResponseClientMock, // the client will not be called anyway + indexName: '.kibana_8.0.0', + discardUnknownDocs: false, + excludeOnUpgradeQuery: initialExcludeOnUpgradeQueryMock, + excludeFromUpgradeFilterHooks, + hookTimeoutMs: 50, + knownTypes: ['foo', 'bar'], + removedTypes: ['server', 'deprecated'], + }); + + await task(); + + expect(checkForUnknownDocs).toHaveBeenCalledTimes(1); + expect(checkForUnknownDocs).toHaveBeenCalledWith({ + client: emptyResponseClientMock, + indexName: '.kibana_8.0.0', + excludeOnUpgradeQuery: initialExcludeOnUpgradeQueryMock, + knownTypes: ['foo', 'bar'], + }); + }); + + it('fails if there are unknown docs and `discardUnknownDocs === false`', async () => { + mockCheckForUnknownDocs.mockReturnValueOnce(async () => + Either.right({ + type: 'unknown_docs_found', + unknownDocs, + }) + ); + + const task = cleanupUnknownAndExcluded({ + client: emptyResponseClientMock, + indexName: '.kibana_8.0.0', + discardUnknownDocs: false, + excludeOnUpgradeQuery: initialExcludeOnUpgradeQueryMock, + excludeFromUpgradeFilterHooks, + hookTimeoutMs: 50, + knownTypes: ['foo', 'bar'], + removedTypes: ['server', 'deprecated'], + }); + + const result = await task(); + + expect(Either.isLeft(result)).toBe(true); + expect((result as Either.Left).left).toEqual({ + type: 'unknown_docs_found', + unknownDocs, + }); + expect(calculateExcludeFilters).not.toHaveBeenCalled(); + expect(deleteByQuery).not.toHaveBeenCalled(); + }); + + describe('if there are no unknown documents', () => { + it('calls `Actions.calculateExcludeFilters()` with the correct params', async () => { + mockCheckForUnknownDocs.mockReturnValueOnce(async () => Either.right({})); + mockCalculateExcludeFilters.mockReturnValueOnce(async () => + Either.right({ + filterClauses: [], + errorsByType: {}, + }) + ); + mockDeleteByQuery.mockReturnValueOnce(async () => + Either.right({ + taskId: '1234', + }) + ); + const task = cleanupUnknownAndExcluded({ + client: emptyResponseClientMock, + indexName: '.kibana_8.0.0', + discardUnknownDocs: false, + excludeOnUpgradeQuery: initialExcludeOnUpgradeQueryMock, + excludeFromUpgradeFilterHooks, + hookTimeoutMs: 50, + knownTypes: ['foo', 'bar'], + removedTypes: ['server', 'deprecated'], + }); + + await task(); + + expect(calculateExcludeFilters).toHaveBeenCalledTimes(1); + expect(calculateExcludeFilters).toHaveBeenCalledWith({ + client: emptyResponseClientMock, + excludeFromUpgradeFilterHooks, + hookTimeoutMs: 50, + }); + }); + }); + + describe('if there are unknown documents and `discardUnknownDocuments === true`', () => { + it('calls `Actions.calculateExcludeFilters()` with the correct params', async () => { + mockCheckForUnknownDocs.mockReturnValueOnce(async () => + Either.right({ + type: 'unknown_docs_found', + unknownDocs, + }) + ); + mockCalculateExcludeFilters.mockReturnValueOnce(async () => + Either.right({ + filterClauses: [], + errorsByType: {}, + }) + ); + mockDeleteByQuery.mockReturnValueOnce(async () => + Either.right({ + taskId: '1234', + }) + ); + const task = cleanupUnknownAndExcluded({ + client: emptyResponseClientMock, + indexName: '.kibana_8.0.0', + discardUnknownDocs: true, + excludeOnUpgradeQuery: initialExcludeOnUpgradeQueryMock, + excludeFromUpgradeFilterHooks, + hookTimeoutMs: 28, + knownTypes: ['foo', 'bar'], + removedTypes: ['server', 'deprecated'], + }); + + await task(); + + expect(calculateExcludeFilters).toHaveBeenCalledTimes(1); + expect(calculateExcludeFilters).toHaveBeenCalledWith({ + client: emptyResponseClientMock, + excludeFromUpgradeFilterHooks, + hookTimeoutMs: 28, + }); + }); + }); + + it('calls `deleteByQuery` with the correct params', async () => { + mockCheckForUnknownDocs.mockReturnValueOnce(async () => + Either.right({ + type: 'unknown_docs_found', + unknownDocs, + }) + ); + + const filterClauses: QueryDslQueryContainer[] = [ + { + bool: { + must: [ + { term: { type: 'search-session' } }, + { match: { 'search-session.persisted': false } }, + ], + }, + }, + ]; + + const errorsByType = { type1: new Error('an error!') }; + + mockCalculateExcludeFilters.mockReturnValueOnce(async () => + Either.right({ filterClauses, errorsByType }) + ); + mockDeleteByQuery.mockReturnValueOnce(async () => + Either.right({ + taskId: '1234', + }) + ); + const task = cleanupUnknownAndExcluded({ + client: emptyResponseClientMock, + indexName: '.kibana_8.0.0', + discardUnknownDocs: true, + excludeOnUpgradeQuery: initialExcludeOnUpgradeQueryMock, + excludeFromUpgradeFilterHooks, + hookTimeoutMs: 28, + knownTypes: ['foo', 'bar'], + removedTypes: ['server', 'deprecated'], + }); + + const result = await task(); + + expect(deleteByQuery).toHaveBeenCalledTimes(1); + expect(deleteByQuery).toHaveBeenCalledWith({ + client: emptyResponseClientMock, + indexName: '.kibana_8.0.0', + query: { + bool: { + should: [ + // excluded from upgrade hook response + { + bool: { + must: [ + { term: { type: 'search-session' } }, + { match: { 'search-session.persisted': false } }, + ], + }, + }, + { term: { type: 'server' } }, // removed type + { term: { type: 'deprecated' } }, // removed type + { term: { type: 'dashboard' } }, // unknown type + ], + }, + }, + conflicts: 'proceed', + refresh: false, + }); + + expect(Either.isRight(result)).toBe(true); + expect((result as Either.Right).right).toEqual({ + type: 'cleanup_started' as const, + taskId: '1234', + unknownDocs, + errorsByType, + }); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/cleanup_unknown_and_excluded.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/cleanup_unknown_and_excluded.ts new file mode 100644 index 0000000000000..d7ceeec014ddc --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/cleanup_unknown_and_excluded.ts @@ -0,0 +1,133 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 * as Either from 'fp-ts/lib/Either'; +import * as TaskEither from 'fp-ts/lib/TaskEither'; +import { pipe } from 'fp-ts/lib/function'; +import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; +import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; +import type { SavedObjectTypeExcludeFromUpgradeFilterHook } from '@kbn/core-saved-objects-server'; +import type { RetryableEsClientError } from './catch_retryable_es_client_errors'; +import { + checkForUnknownDocs, + type DocumentIdAndType, + type UnknownDocsFound, +} from './check_for_unknown_docs'; +import { isTypeof } from '.'; +import { CalculatedExcludeFilter, calculateExcludeFilters } from './calculate_exclude_filters'; +import { deleteByQuery } from './delete_by_query'; + +/** @internal */ +export interface CleanupUnknownAndExcludedParams { + client: ElasticsearchClient; + indexName: string; + discardUnknownDocs: boolean; + excludeOnUpgradeQuery: QueryDslQueryContainer; + excludeFromUpgradeFilterHooks: Record; + hookTimeoutMs?: number; + knownTypes: string[]; + removedTypes: string[]; +} + +/** @internal */ +export interface CleanupStarted { + type: 'cleanup_started'; + /** Sample (1000 types * 100 docs per type) of the unknown documents that have been found */ + unknownDocs: DocumentIdAndType[]; + /** Any errors that were encountered during filter calculation, keyed by the type name */ + errorsByType: Record; + /** the id of the asynchronous delete task */ + taskId: string; +} + +/** + * Cleans up unknown and excluded types from the specified index. + */ +export const cleanupUnknownAndExcluded = ({ + client, + indexName, + discardUnknownDocs, + excludeOnUpgradeQuery, + excludeFromUpgradeFilterHooks, + hookTimeoutMs, + knownTypes, + removedTypes, +}: CleanupUnknownAndExcludedParams): TaskEither.TaskEither< + RetryableEsClientError | UnknownDocsFound, + CleanupStarted +> => { + let unknownDocs: DocumentIdAndType[] = []; + let unknownDocTypes: string[] = []; + let errorsByType: Record = {}; + + return pipe( + // check if there are unknown docs + checkForUnknownDocs({ client, indexName, knownTypes, excludeOnUpgradeQuery }), + + // make sure we are allowed to get rid of them (in case there are some) + TaskEither.chainEitherKW((unknownDocsRes: {} | UnknownDocsFound) => { + if (isTypeof(unknownDocsRes, 'unknown_docs_found')) { + unknownDocs = unknownDocsRes.unknownDocs; + unknownDocTypes = [...new Set(unknownDocs.map(({ type }) => type))]; + if (!discardUnknownDocs) { + return Either.left({ + type: 'unknown_docs_found' as const, + unknownDocs: unknownDocsRes.unknownDocs, + }); + } + } + return Either.right(undefined); + }), + + // calculate exclude filters (we use them to build the query for documents that must be deleted) + TaskEither.chainW( + (): TaskEither.TaskEither => + calculateExcludeFilters({ client, excludeFromUpgradeFilterHooks, hookTimeoutMs }) + ), + + // actively delete unwanted documents + TaskEither.chainW((excludeFiltersRes) => { + errorsByType = excludeFiltersRes.errorsByType; + + // we must delete everything that matches: + // - any of the plugin-defined exclude filters + // - OR any of the unknown types + const deleteQuery: QueryDslQueryContainer = { + bool: { + should: [ + ...excludeFiltersRes.filterClauses, + ...removedTypes.map((type) => ({ term: { type } })), + ...unknownDocTypes.map((type) => ({ term: { type } })), + ], + }, + }; + + return deleteByQuery({ + client, + indexName, + query: deleteQuery, + // we want to delete as many docs as we can in the current attempt + conflicts: 'proceed', + // instead of forcing refresh after each delete attempt, + // we opt for a delayRetry mechanism when conflicts appear, + // letting the periodic refresh kick in + refresh: false, + }); + }), + + // map response output + TaskEither.chainEitherKW((res) => { + return Either.right({ + type: 'cleanup_started' as const, + taskId: res.taskId, + unknownDocs, + errorsByType, + }); + }) + ); +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/delete_by_query.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/delete_by_query.test.ts new file mode 100644 index 0000000000000..784edfdc67b6a --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/delete_by_query.test.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 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 * as Either from 'fp-ts/lib/Either'; +import { catchRetryableEsClientErrors } from './catch_retryable_es_client_errors'; +import { errors as EsErrors } from '@elastic/elasticsearch'; +import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; +import { deleteByQuery } from './delete_by_query'; + +jest.mock('./catch_retryable_es_client_errors'); + +describe('deleteByQuery', () => { + const deleteQuery = { + bool: { + should: ['server', 'deprecated'].map((type) => ({ + term: { + type, + }, + })), + }, + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('calls catchRetryableEsClientErrors when the promise rejects', async () => { + // Create a mock client that rejects all methods with a 503 status code response. + const retryableError = new EsErrors.ResponseError( + elasticsearchClientMock.createApiResponse({ + statusCode: 503, + body: { error: { type: 'es_type', reason: 'es_reason' } }, + }) + ); + const client = elasticsearchClientMock.createInternalClient( + elasticsearchClientMock.createErrorTransportRequestPromise(retryableError) + ); + + const task = deleteByQuery({ + client, + indexName: '.kibana_8.0.0', + query: deleteQuery, + conflicts: 'proceed', + refresh: true, + }); + try { + await task(); + } catch (e) { + /** ignore */ + } + expect(catchRetryableEsClientErrors).toHaveBeenCalledWith(retryableError); + }); + + it('calls `client.deleteByQuery` with the correct parameters', async () => { + const client = elasticsearchClientMock.createInternalClient( + Promise.resolve({ hits: { hits: [] } }) + ); + + const task = deleteByQuery({ + client, + indexName: '.kibana_8.0.0', + query: deleteQuery, + conflicts: 'proceed', + refresh: true, + }); + + await task(); + + expect(client.deleteByQuery).toHaveBeenCalledTimes(1); + expect(client.deleteByQuery).toHaveBeenCalledWith({ + index: '.kibana_8.0.0', + query: deleteQuery, + refresh: true, + wait_for_completion: false, + conflicts: 'proceed', + }); + }); + + it('resolves with `Either.right` if the delete task is successfully created', async () => { + const client = elasticsearchClientMock.createInternalClient( + Promise.resolve({ + took: 147, + timed_out: false, + task: 1234, + }) + ); + + const task = deleteByQuery({ + client, + indexName: '.kibana_8.0.0', + query: deleteQuery, + conflicts: 'proceed', + refresh: true, + }); + + const result = await task(); + + expect(Either.isRight(result)).toBe(true); + expect((result as Either.Right).right).toEqual({ taskId: '1234' }); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/delete_by_query.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/delete_by_query.ts new file mode 100644 index 0000000000000..f9c426c26696d --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/delete_by_query.ts @@ -0,0 +1,56 @@ +/* + * Copyright 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 * as Either from 'fp-ts/lib/Either'; +import * as TaskEither from 'fp-ts/lib/TaskEither'; +import type { Conflicts, QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; +import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; +import { + catchRetryableEsClientErrors, + type RetryableEsClientError, +} from './catch_retryable_es_client_errors'; + +/** @internal */ +export interface DeleteByQueryParams { + client: ElasticsearchClient; + indexName: string; + query: QueryDslQueryContainer; + conflicts: Conflicts; + refresh?: boolean; +} + +/** @internal */ +export interface DeleteByQueryResponse { + taskId: string; +} + +/** + * Deletes documents matching the provided query + */ +export const deleteByQuery = + ({ + client, + indexName, + query, + conflicts, + refresh = false, + }: DeleteByQueryParams): TaskEither.TaskEither => + () => { + return client + .deleteByQuery({ + index: indexName, + query, + refresh, + conflicts, + wait_for_completion: false, + }) + .then(({ task: taskId }) => { + return Either.right({ taskId: String(taskId!) }); + }) + .catch(catchRetryableEsClientErrors); + }; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/index.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/index.ts index 6b6db29563443..2593ac7867d1e 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/index.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/index.ts @@ -76,6 +76,10 @@ import type { AliasNotFound, RemoveIndexNotAConcreteIndex } from './update_alias export type { AliasAction, UpdateAliasesParams } from './update_aliases'; export { updateAliases } from './update_aliases'; +export { cleanupUnknownAndExcluded } from './cleanup_unknown_and_excluded'; + +export { waitForDeleteByQueryTask } from './wait_for_delete_by_query_task'; + export type { CreateIndexParams } from './create_index'; export { createIndex } from './create_index'; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/wait_for_delete_by_query_task.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/wait_for_delete_by_query_task.test.ts new file mode 100644 index 0000000000000..8f9b60cfe02d1 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/wait_for_delete_by_query_task.test.ts @@ -0,0 +1,199 @@ +/* + * Copyright 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 * as Either from 'fp-ts/lib/Either'; +import * as TaskEither from 'fp-ts/lib/TaskEither'; +import * as Option from 'fp-ts/lib/Option'; +import { errors as EsErrors, TransportResult } from '@elastic/elasticsearch'; +import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; +import { waitForDeleteByQueryTask } from './wait_for_delete_by_query_task'; +import { waitForTask } from './wait_for_task'; + +jest.mock('./wait_for_task'); + +const mockWaitForTask = waitForTask as jest.MockedFunction; + +describe('waitForDeleteByQueryTask', () => { + const client = elasticsearchClientMock.createInternalClient( + Promise.resolve(elasticsearchClientMock.createApiResponse({})) + ); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('calls waitForTask() with the appropriate params', async () => { + // Mock wait for delete finished successfully + mockWaitForTask.mockReturnValueOnce( + TaskEither.right({ + completed: true, + error: Option.none, + failures: Option.none, + description: 'some description', + }) + ); + + const task = waitForDeleteByQueryTask({ + client, + taskId: 'some task id', + timeout: '60s', + }); + + await task(); + + expect(waitForTask).toHaveBeenCalledWith({ + client, + taskId: 'some task id', + timeout: '60s', + }); + }); + + describe('when waitForTask() method rejects with a task completion timeout error', () => { + it('catches the error and returns the appropriate Left response', async () => { + // Mock task completion error + const error = createError({ + body: { error: { type: 'timeout_exception', reason: 'es_reason' } }, + }); + + mockWaitForTask.mockReturnValueOnce( + TaskEither.left({ + type: 'wait_for_task_completion_timeout' as const, + message: '[timeout_exception] es_reason', + error, + }) + ); + + const task = waitForDeleteByQueryTask({ + client, + taskId: 'my task id', + timeout: '60s', + }); + + const res = await task(); + + expect(res).toEqual( + Either.left({ + type: 'wait_for_task_completion_timeout' as const, + message: '[timeout_exception] es_reason', + error, + }) + ); + }); + }); + + describe('when waitForTask() method rejects with a retryable error', () => { + it('catches the error and returns the appropriate Left response', async () => { + // Mock retryable error + const error = createError({ + statusCode: 503, + body: { error: { type: 'es_type', reason: 'es_reason' } }, + }); + + mockWaitForTask.mockReturnValueOnce( + TaskEither.left({ + type: 'retryable_es_client_error' as const, + message: 'es_type', + error, + }) + ); + + const task = waitForDeleteByQueryTask({ + client, + taskId: 'my task id', + timeout: '60s', + }); + + const res = await task(); + expect(res).toEqual( + Either.left({ + type: 'retryable_es_client_error' as const, + message: 'es_type', + error, + }) + ); + }); + }); + + describe('when waitForTask() method finishes successfully, but there are failures', () => { + it('returns a Left response, with the list of failures', async () => { + // Mock successful with failures + const failures = ['dashboard:12345 - Failed to delete', 'dashboard:67890 - Failed to delete']; + + mockWaitForTask.mockReturnValueOnce( + TaskEither.right({ + completed: true, + failures: Option.some(failures), + error: Option.none, + }) + ); + + const task = waitForDeleteByQueryTask({ + client, + taskId: 'my task id', + timeout: '60s', + }); + + const res = await task(); + expect(res).toEqual( + Either.left({ + type: 'cleanup_failed' as const, + failures, + }) + ); + }); + }); + + describe('when waitForTask() method throws an unexpected error', () => { + it('rethrows the error', async () => { + // Mock unexpected 500 Server Error + const error = createError({ + statusCode: 500, + body: { error: { type: 'server_error', reason: 'Something really bad happened' } }, + }); + + mockWaitForTask.mockReturnValueOnce(async () => { + throw error; + }); + + const task = waitForDeleteByQueryTask({ + client, + taskId: 'my task id', + timeout: '60s', + }); + + expect(task()).rejects.toEqual(error); + }); + }); + + describe('when waitForTask() method finishes successfully without failures', () => { + it('finsihes with a cleanup_successful Right clause', async () => { + // Mock wait for delete finished successfully + mockWaitForTask.mockReturnValueOnce( + TaskEither.right({ + completed: true, + error: Option.none, + failures: Option.none, + description: 'some description', + }) + ); + const task = waitForDeleteByQueryTask({ + client, + taskId: 'my task id', + timeout: '60s', + }); + + const res = await task(); + + expect(res).toEqual(Either.right({ type: 'cleanup_successful' as const })); + }); + }); +}); + +const createError = (esResponse: Partial) => { + return new EsErrors.ResponseError(elasticsearchClientMock.createApiResponse(esResponse)); +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/wait_for_delete_by_query_task.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/wait_for_delete_by_query_task.ts new file mode 100644 index 0000000000000..5fae1283b083b --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/wait_for_delete_by_query_task.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 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 * as TaskEither from 'fp-ts/lib/TaskEither'; +import * as Option from 'fp-ts/lib/Option'; +import { flow } from 'fp-ts/lib/function'; +import { waitForTask } from './wait_for_task'; + +/** @internal */ +export interface CleanupErrorResponse { + type: 'cleanup_failed'; + failures: string[]; + versionConflicts?: number; +} + +/** @internal */ +export interface CleanupSuccessfulResponse { + type: 'cleanup_successful'; + deleted?: number; +} + +export const waitForDeleteByQueryTask = flow( + waitForTask, + TaskEither.chainW( + (res): TaskEither.TaskEither => { + if (Option.isSome(res.failures) || res.response?.version_conflicts) { + return TaskEither.left({ + type: 'cleanup_failed' as const, + failures: Option.isSome(res.failures) ? res.failures.value : [], + versionConflicts: res.response?.version_conflicts, + }); + } else if (Option.isSome(res.error)) { + throw new Error( + 'waitForDeleteByQueryTask task failed with the following error:\n' + + JSON.stringify(res.error.value) + ); + } else { + return TaskEither.right({ + type: 'cleanup_successful' as const, + deleted: res.response?.deleted, + }); + } + } + ) +); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/wait_for_pickup_updated_mappings_task.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/wait_for_pickup_updated_mappings_task.test.ts index 896a687c1f8d3..66d7689a0b9e4 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/wait_for_pickup_updated_mappings_task.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/wait_for_pickup_updated_mappings_task.test.ts @@ -10,32 +10,33 @@ import { catchRetryableEsClientErrors } from './catch_retryable_es_client_errors import { errors as EsErrors } from '@elastic/elasticsearch'; import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; import { waitForPickupUpdatedMappingsTask } from './wait_for_pickup_updated_mappings_task'; -import { setWriteBlock } from './set_write_block'; -jest.mock('./catch_retryable_es_client_errors'); +jest.mock('./catch_retryable_es_client_errors', () => { + const { catchRetryableEsClientErrors: actualImplementation } = jest.requireActual( + './catch_retryable_es_client_errors' + ); + return { + catchRetryableEsClientErrors: jest.fn(actualImplementation), + }; +}); describe('waitForPickupUpdatedMappingsTask', () => { beforeEach(() => { jest.clearAllMocks(); }); - // Create a mock client that rejects all methods with a 503 status code - // response. - const retryableError = new EsErrors.ResponseError( - elasticsearchClientMock.createApiResponse({ - statusCode: 503, - body: { error: { type: 'es_type', reason: 'es_reason' } }, - }) - ); - const client = elasticsearchClientMock.createInternalClient( - elasticsearchClientMock.createErrorTransportRequestPromise(retryableError) - ); - - const nonRetryableError = new Error('crash'); - const clientWithNonRetryableError = elasticsearchClientMock.createInternalClient( - elasticsearchClientMock.createErrorTransportRequestPromise(nonRetryableError) - ); it('calls catchRetryableEsClientErrors when the promise rejects', async () => { + // Create a mock client that rejects all methods with a 503 status code + // response. + const retryableError = new EsErrors.ResponseError( + elasticsearchClientMock.createApiResponse({ + statusCode: 503, + body: { error: { type: 'es_type', reason: 'es_reason' } }, + }) + ); + const client = elasticsearchClientMock.createInternalClient( + elasticsearchClientMock.createErrorTransportRequestPromise(retryableError) + ); const task = waitForPickupUpdatedMappingsTask({ client, taskId: 'my task id', @@ -50,11 +51,16 @@ describe('waitForPickupUpdatedMappingsTask', () => { expect(catchRetryableEsClientErrors).toHaveBeenCalledWith(retryableError); }); it('re-throws non retry-able errors', async () => { - const task = setWriteBlock({ - client: clientWithNonRetryableError, - index: 'my_index', + const nonRetryableError = new Error('crash'); + const client = elasticsearchClientMock.createInternalClient( + elasticsearchClientMock.createErrorTransportRequestPromise(nonRetryableError) + ); + + const task = waitForPickupUpdatedMappingsTask({ + client, + taskId: 'my task id', + timeout: '2m', }); - await task(); - expect(catchRetryableEsClientErrors).toHaveBeenCalledWith(nonRetryableError); + expect(task()).rejects.toThrowError(nonRetryableError); }); }); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/wait_for_task.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/wait_for_task.test.ts index 4a5fc20e1fe12..61611dc5afc81 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/wait_for_task.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/wait_for_task.test.ts @@ -5,44 +5,113 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { errors as EsErrors } from '@elastic/elasticsearch'; -import { waitForTask } from './wait_for_task'; -import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; -import { catchRetryableEsClientErrors } from './catch_retryable_es_client_errors'; -jest.mock('./catch_retryable_es_client_errors'); +import * as Either from 'fp-ts/lib/Either'; +import { errors as EsErrors, TransportResult } from '@elastic/elasticsearch'; +import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; +import { waitForTask } from './wait_for_task'; describe('waitForTask', () => { beforeEach(() => { jest.clearAllMocks(); }); - // Create a mock client that rejects all methods with a 503 status code - // response. - const retryableError = new EsErrors.ResponseError( - elasticsearchClientMock.createApiResponse({ + it('calls tasks API get() with the correct parameters', async () => { + // Mock client that rejects with a retryable error + const { client } = createErrorClient({ statusCode: 503, body: { error: { type: 'es_type', reason: 'es_reason' } }, - }) - ); - const client = elasticsearchClientMock.createInternalClient( - elasticsearchClientMock.createErrorTransportRequestPromise(retryableError) - ); + }); + + const task = waitForTask({ + client, + taskId: 'my task id', + timeout: '60s', + }); + + await task(); + expect(client.tasks.get).toHaveBeenCalledTimes(1); + expect(client.tasks.get).toHaveBeenCalledWith({ + task_id: 'my task id', + wait_for_completion: true, + timeout: '60s', + }); + }); + + describe('when tasks API get() method rejects with a task completion timeout error', () => { + it('catches the error and returns the appropriate Left response', async () => { + // Mock client that rejects with a task completion timeout error + const { client, error } = createErrorClient({ + body: { error: { type: 'timeout_exception', reason: 'es_reason' } }, + }); + + const task = waitForTask({ + client, + taskId: 'my task id', + timeout: '60s', + }); + + const res = await task(); + + expect(res).toEqual( + Either.left({ + type: 'wait_for_task_completion_timeout' as const, + message: '[timeout_exception] es_reason', + error, + }) + ); + }); + }); + + describe('when tasks API get() method rejects with a retryable error', () => { + it('catches the error and returns the appropriate Left response', async () => { + // Mock client that rejects with a 503 status code + const { client, error } = createErrorClient({ + statusCode: 503, + body: { error: { type: 'es_type', reason: 'es_reason' } }, + }); - describe('waitForPickupUpdatedMappingsTask', () => { - it('calls catchRetryableEsClientErrors when the promise rejects', async () => { const task = waitForTask({ client, taskId: 'my task id', timeout: '60s', }); - try { - await task(); - } catch (e) { - /** ignore */ - } - expect(catchRetryableEsClientErrors).toHaveBeenCalledWith(retryableError); + const res = await task(); + expect(res).toEqual( + Either.left({ + type: 'retryable_es_client_error' as const, + message: 'es_type', + error, + }) + ); + }); + }); + + describe('when tasks API get() method rejects with an unexpected error', () => { + it('rethrows the error', async () => { + // Mock client that rejects with a 500 Server Error + const { client, error } = createErrorClient({ + statusCode: 500, + body: { error: { type: 'server_error', reason: 'Something really bad happened' } }, + }); + + const task = waitForTask({ + client, + taskId: 'my task id', + timeout: '60s', + }); + + expect(task()).rejects.toEqual(error); }); }); }); + +const createErrorClient = (esResponse: Partial) => { + const error = new EsErrors.ResponseError(elasticsearchClientMock.createApiResponse(esResponse)); + const client = elasticsearchClientMock.createInternalClient( + elasticsearchClientMock.createErrorTransportRequestPromise(error) + ); + + return { client, error }; +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/wait_for_task.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/wait_for_task.ts index 8bb3abaec87db..86389f01727b7 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/wait_for_task.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/wait_for_task.ts @@ -5,7 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type * as estypes from '@elastic/elasticsearch/lib/api/types'; import * as Either from 'fp-ts/lib/Either'; import * as TaskEither from 'fp-ts/lib/TaskEither'; import * as Option from 'fp-ts/lib/Option'; @@ -22,6 +22,7 @@ export interface WaitForTaskResponse { completed: boolean; failures: Option.Option; description?: string; + response?: estypes.TasksTaskStatus; } /** @@ -90,6 +91,7 @@ export const waitForTask = error: Option.fromNullable(body.error as estypes.ErrorCauseKeys), failures: failures.length > 0 ? Option.some(failures) : Option.none, description: body.task.description, + response: body.response, }); }) .catch(catchWaitForTaskCompletionTimeout) diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/common/utils/index.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/common/utils/index.ts new file mode 100644 index 0000000000000..962f40b87db02 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/common/utils/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { logActionResponse, logStateTransition, type LogAwareState } from './logs'; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/common/utils/logs.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/common/utils/logs.ts new file mode 100644 index 0000000000000..2f3a1459f594c --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/common/utils/logs.ts @@ -0,0 +1,71 @@ +/* + * Copyright 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 { Logger, LogMeta } from '@kbn/logging'; +import { MigrationLog } from '../../types'; + +export interface LogAwareState { + controlState: string; + logs: MigrationLog[]; +} + +interface StateTransitionLogMeta extends LogMeta { + kibana: { + migrations: { + state: LogAwareState; + duration: number; + }; + }; +} + +export const logStateTransition = ( + logger: Logger, + logMessagePrefix: string, + prevState: LogAwareState, + currState: LogAwareState, + tookMs: number +) => { + if (currState.logs.length > prevState.logs.length) { + currState.logs.slice(prevState.logs.length).forEach(({ message, level }) => { + switch (level) { + case 'error': + return logger.error(logMessagePrefix + message); + case 'warning': + return logger.warn(logMessagePrefix + message); + case 'info': + return logger.info(logMessagePrefix + message); + default: + throw new Error(`unexpected log level ${level}`); + } + }); + } + + logger.info( + logMessagePrefix + `${prevState.controlState} -> ${currState.controlState}. took: ${tookMs}ms.` + ); + logger.debug( + logMessagePrefix + `${prevState.controlState} -> ${currState.controlState}. took: ${tookMs}ms.`, + { + kibana: { + migrations: { + state: currState, + duration: tookMs, + }, + }, + } + ); +}; + +export const logActionResponse = ( + logger: Logger, + logMessagePrefix: string, + state: LogAwareState, + res: unknown +) => { + logger.debug(logMessagePrefix + `${state.controlState} RESPONSE`, res as LogMeta); +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/build_types_mappings.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/build_types_mappings.ts new file mode 100644 index 0000000000000..6c78ae52550c5 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/build_types_mappings.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 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 { SavedObjectsType } from '@kbn/core-saved-objects-server'; +import type { SavedObjectsTypeMappingDefinitions } from '@kbn/core-saved-objects-base-server-internal'; + +/** + * Merge mappings from all registered saved object types. + */ +export const buildTypesMappings = ( + types: SavedObjectsType[] +): SavedObjectsTypeMappingDefinitions => { + return types.reduce((acc, { name: type, mappings }) => { + const duplicate = acc.hasOwnProperty(type); + if (duplicate) { + throw new Error(`Type ${type} is already defined.`); + } + return { + ...acc, + [type]: mappings, + }; + }, {}); +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/index.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/index.ts index 81bacea4a0cbd..a113e5e5f77bc 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/index.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/index.ts @@ -11,6 +11,8 @@ export type { LogFn } from './migration_logger'; export { excludeUnusedTypesQuery, REMOVED_TYPES } from './unused_types'; export { TransformSavedObjectDocumentError } from './transform_saved_object_document_error'; export { deterministicallyRegenerateObjectId } from './regenerate_object_id'; +export { buildTypesMappings } from './build_types_mappings'; +export { createIndexMap, type IndexMap, type CreateIndexMapOptions } from './build_index_map'; export type { DocumentsTransformFailed, DocumentsTransformSuccess, diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/unused_types.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/unused_types.ts index e506ff40073fc..46ddd23251217 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/unused_types.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/unused_types.ts @@ -45,12 +45,10 @@ export const REMOVED_TYPES: string[] = [ 'maps-telemetry', // Deprecated, no longer used since 8.7 https://github.com/elastic/kibana/pull/148530 'csp_rule', + // Removed in 8.8 https://github.com/elastic/kibana/pull/151116 + 'upgrade-assistant-telemetry', ].sort(); -// When migrating from the outdated index we use a read query which excludes -// saved objects which are no longer used. These saved objects will still be -// kept in the outdated index for backup purposes, but won't be available in -// the upgraded index. export const excludeUnusedTypesQuery: QueryDslQueryContainer = { bool: { must_not: [ @@ -59,23 +57,6 @@ export const excludeUnusedTypesQuery: QueryDslQueryContainer = { type: typeName, }, })), - // https://github.com/elastic/kibana/issues/96131 - { - bool: { - must: [ - { - match: { - type: 'search-session', - }, - }, - { - match: { - 'search-session.persisted': false, - }, - }, - ], - }, - }, ], }, }; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/index.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/index.ts index b50f64a26620d..97e62e8657d5e 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/index.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/index.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -export { KibanaMigrator, mergeTypes } from './kibana_migrator'; +export { KibanaMigrator } from './kibana_migrator'; export type { KibanaMigratorOptions } from './kibana_migrator'; -export { buildActiveMappings } from './core'; +export { buildActiveMappings, buildTypesMappings } from './core'; export { DocumentMigrator } from './document_migrator'; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/initial_state.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/initial_state.test.ts index 27798706e8fd1..2961600edd61d 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/initial_state.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/initial_state.test.ts @@ -164,19 +164,8 @@ describe('createInitialState', () => { }, }, Object { - "bool": Object { - "must": Array [ - Object { - "match": Object { - "type": "search-session", - }, - }, - Object { - "match": Object { - "search-session.persisted": false, - }, - }, - ], + "term": Object { + "type": "upgrade-assistant-telemetry", }, }, ], diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator.test.ts index 655f164b831cf..f5a462ebcedac 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator.test.ts @@ -281,6 +281,7 @@ const mockOptions = () => { ]), kibanaIndex: '.my-index', soMigrationsConfig: { + algorithm: 'v2', batchSize: 20, maxBatchSizeBytes: ByteSizeValue.parse('20mb'), pollInterval: 20000, diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator.ts index fa0c88629758b..408b277444995 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator.ts @@ -16,7 +16,6 @@ import Semver from 'semver'; import type { Logger } from '@kbn/logging'; import type { DocLinksServiceStart } from '@kbn/core-doc-links-server'; import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; -import type { SavedObjectsType } from '@kbn/core-saved-objects-server'; import type { SavedObjectUnsanitizedDoc, SavedObjectsRawDoc, @@ -31,11 +30,12 @@ import { type KibanaMigratorStatus, type MigrationResult, } from '@kbn/core-saved-objects-base-server-internal'; -import { buildActiveMappings } from './core'; +import { buildActiveMappings, buildTypesMappings } from './core'; import { DocumentMigrator, type VersionedTransformer } from './document_migrator'; import { createIndexMap } from './core/build_index_map'; import { runResilientMigrator } from './run_resilient_migrator'; import { migrateRawDocsSafely } from './core/migrate_raw_docs'; +import { runZeroDowntimeMigration } from './zdt'; // ensure plugins don't try to convert SO namespaceTypes after 8.0.0 // see https://github.com/elastic/kibana/issues/147344 @@ -91,7 +91,7 @@ export class KibanaMigrator implements IKibanaMigrator { this.soMigrationsConfig = soMigrationsConfig; this.typeRegistry = typeRegistry; this.serializer = new SavedObjectsSerializer(this.typeRegistry); - this.mappingProperties = mergeTypes(this.typeRegistry.getAllTypes()); + this.mappingProperties = buildTypesMappings(this.typeRegistry.getAllTypes()); this.log = logger; this.kibanaVersion = kibanaVersion; this.documentMigrator = new DocumentMigrator({ @@ -135,6 +135,28 @@ export class KibanaMigrator implements IKibanaMigrator { } private runMigrationsInternal(): Promise { + const migrationAlgorithm = this.soMigrationsConfig.algorithm; + if (migrationAlgorithm === 'zdt') { + return this.runMigrationZdt(); + } else { + return this.runMigrationV2(); + } + } + + private runMigrationZdt(): Promise { + return runZeroDowntimeMigration({ + kibanaIndexPrefix: this.kibanaIndex, + typeRegistry: this.typeRegistry, + logger: this.log, + documentMigrator: this.documentMigrator, + migrationConfig: this.soMigrationsConfig, + docLinks: this.docLinks, + serializer: this.serializer, + elasticsearchClient: this.client, + }); + } + + private runMigrationV2(): Promise { const indexMap = createIndexMap({ kibanaIndexName: this.kibanaIndex, indexMap: this.mappingProperties, @@ -187,20 +209,3 @@ export class KibanaMigrator implements IKibanaMigrator { return this.documentMigrator.migrate(doc); } } - -/** - * Merges savedObjectMappings properties into a single object, verifying that - * no mappings are redefined. - */ -export function mergeTypes(types: SavedObjectsType[]): SavedObjectsTypeMappingDefinitions { - return types.reduce((acc, { name: type, mappings }) => { - const duplicate = acc.hasOwnProperty(type); - if (duplicate) { - throw new Error(`Type ${type} is already defined.`); - } - return { - ...acc, - [type]: mappings, - }; - }, {}); -} diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/migrations_state_action_machine.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/migrations_state_action_machine.test.ts index 255a26275b6e8..48a3bad0d0960 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/migrations_state_action_machine.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/migrations_state_action_machine.test.ts @@ -17,7 +17,7 @@ import * as Either from 'fp-ts/lib/Either'; import * as Option from 'fp-ts/lib/Option'; import { errors } from '@elastic/elasticsearch'; import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; -import { AllControlStates, State } from './state'; +import type { AllControlStates, State } from './state'; import { createInitialState } from './initial_state'; import { ByteSizeValue } from '@kbn/config-schema'; @@ -44,6 +44,7 @@ describe('migrationsStateActionMachine', () => { migrationVersionPerType: {}, indexPrefix: '.my-so-index', migrationsConfig: { + algorithm: 'v2', batchSize: 1000, maxBatchSizeBytes: new ByteSizeValue(1e8), pollInterval: 0, @@ -102,7 +103,9 @@ describe('migrationsStateActionMachine', () => { ...initialState, reason: 'the fatal reason', outdatedDocuments: [{ _id: '1234', password: 'sensitive password' }], - transformedDocBatches: [[{ _id: '1234', password: 'sensitive transformed password' }]], + bulkOperationBatches: [ + [[{ index: { _id: '1234' } }, { password: 'sensitive transformed password' }]], + ], } as State, logger: mockLogger.get(), model: transitionModel(['LEGACY_DELETE', 'FATAL']), diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/migrations_state_action_machine.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/migrations_state_action_machine.ts index ef9db961c8112..1b5caf3c4e75d 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/migrations_state_action_machine.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/migrations_state_action_machine.ts @@ -8,72 +8,19 @@ import { errors as EsErrors } from '@elastic/elasticsearch'; import * as Option from 'fp-ts/lib/Option'; -import type { Logger, LogMeta } from '@kbn/logging'; +import type { Logger } from '@kbn/logging'; import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import { getErrorMessage, getRequestDebugMeta, } from '@kbn/core-elasticsearch-client-server-internal'; import type { SavedObjectsRawDoc } from '@kbn/core-saved-objects-server'; +import type { BulkOperationContainer } from '@elastic/elasticsearch/lib/api/types'; +import { logActionResponse, logStateTransition } from './common/utils/logs'; import { type Model, type Next, stateActionMachine } from './state_action_machine'; import { cleanup } from './migrations_state_machine_cleanup'; import type { ReindexSourceToTempTransform, ReindexSourceToTempIndexBulk, State } from './state'; - -interface StateTransitionLogMeta extends LogMeta { - kibana: { - migrations: { - state: State; - duration: number; - }; - }; -} - -const logStateTransition = ( - logger: Logger, - logMessagePrefix: string, - prevState: State, - currState: State, - tookMs: number -) => { - if (currState.logs.length > prevState.logs.length) { - currState.logs.slice(prevState.logs.length).forEach(({ message, level }) => { - switch (level) { - case 'error': - return logger.error(logMessagePrefix + message); - case 'warning': - return logger.warn(logMessagePrefix + message); - case 'info': - return logger.info(logMessagePrefix + message); - default: - throw new Error(`unexpected log level ${level}`); - } - }); - } - - logger.info( - logMessagePrefix + `${prevState.controlState} -> ${currState.controlState}. took: ${tookMs}ms.` - ); - logger.debug( - logMessagePrefix + `${prevState.controlState} -> ${currState.controlState}. took: ${tookMs}ms.`, - { - kibana: { - migrations: { - state: currState, - duration: tookMs, - }, - }, - } - ); -}; - -const logActionResponse = ( - logger: Logger, - logMessagePrefix: string, - state: State, - res: unknown -) => { - logger.debug(logMessagePrefix + `${state.controlState} RESPONSE`, res as LogMeta); -}; +import type { BulkOperation } from './model/create_batches'; /** * A specialized migrations-specific state-action machine that: @@ -128,9 +75,9 @@ export async function migrationStateActionMachine({ ), }, ...{ - transformedDocBatches: ( - (newState as ReindexSourceToTempIndexBulk).transformedDocBatches ?? [] - ).map((batches) => batches.map((doc) => ({ _id: doc._id }))) as [SavedObjectsRawDoc[]], + bulkOperationBatches: redactBulkOperationBatches( + (newState as ReindexSourceToTempIndexBulk).bulkOperationBatches ?? [[]] + ), }, }; @@ -212,3 +159,11 @@ export async function migrationStateActionMachine({ } } } + +const redactBulkOperationBatches = ( + bulkOperationBatches: BulkOperation[][] +): BulkOperationContainer[][] => { + return bulkOperationBatches.map((batch) => + batch.map((operation) => (Array.isArray(operation) ? operation[0] : operation)) + ); +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/migrations_state_machine_cleanup.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/migrations_state_machine_cleanup.ts index 8b1f93c327857..c3f6016ea9d9b 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/migrations_state_machine_cleanup.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/migrations_state_machine_cleanup.ts @@ -8,10 +8,13 @@ import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import * as Actions from './actions'; -import type { State } from './state'; -export async function cleanup(client: ElasticsearchClient, state?: State) { - if (!state) return; +type CleanableState = { sourceIndexPitId: string } | {}; + +export async function cleanup(client: ElasticsearchClient, state?: CleanableState) { + if (!state) { + return; + } if ('sourceIndexPitId' in state) { await Actions.closePit({ client, pitId: state.sourceIndexPitId })(); } diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/create_batches.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/create_batches.test.ts index ec9afc31f90d1..3ae3b1c7f20d8 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/create_batches.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/create_batches.test.ts @@ -10,8 +10,13 @@ import type { SavedObjectsRawDoc } from '@kbn/core-saved-objects-server'; import { createBatches } from './create_batches'; describe('createBatches', () => { - const DOCUMENT_SIZE_BYTES = 128; - const INDEX = '.kibana_version_index'; + const documentToOperation = (document: SavedObjectsRawDoc) => [ + { index: { _id: document._id } }, + document._source, + ]; + + const DOCUMENT_SIZE_BYTES = 77; // 76 + \n + it('returns right one batch if all documents fit in maxBatchSizeBytes', () => { const documents = [ { _id: '', _source: { type: 'dashboard', title: 'my saved object title ¹' } }, @@ -19,8 +24,8 @@ describe('createBatches', () => { { _id: '', _source: { type: 'dashboard', title: 'my saved object title ®' } }, ]; - expect(createBatches(documents, INDEX, DOCUMENT_SIZE_BYTES * 3)).toEqual( - Either.right([documents]) + expect(createBatches({ documents, maxBatchSizeBytes: DOCUMENT_SIZE_BYTES * 3 })).toEqual( + Either.right([documents.map(documentToOperation)]) ); }); it('creates multiple batches with each batch limited to maxBatchSizeBytes', () => { @@ -31,32 +36,36 @@ describe('createBatches', () => { { _id: '', _source: { type: 'dashboard', title: 'my saved object title 44' } }, { _id: '', _source: { type: 'dashboard', title: 'my saved object title 55' } }, ]; - expect(createBatches(documents, INDEX, DOCUMENT_SIZE_BYTES * 2)).toEqual( - Either.right([[documents[0], documents[1]], [documents[2], documents[3]], [documents[4]]]) + expect(createBatches({ documents, maxBatchSizeBytes: DOCUMENT_SIZE_BYTES * 2 })).toEqual( + Either.right([ + documents.slice(0, 2).map(documentToOperation), + documents.slice(2, 4).map(documentToOperation), + documents.slice(4).map(documentToOperation), + ]) ); }); it('creates a single empty batch if there are no documents', () => { const documents = [] as SavedObjectsRawDoc[]; - expect(createBatches(documents, INDEX, 100)).toEqual(Either.right([[]])); + expect(createBatches({ documents, maxBatchSizeBytes: 100 })).toEqual(Either.right([[]])); }); it('throws if any one document exceeds the maxBatchSizeBytes', () => { const documents = [ - { _id: '', _source: { type: 'dashboard', title: 'my saved object title ¹' } }, + { _id: 'foo', _source: { type: 'dashboard', title: 'my saved object title ¹' } }, { - _id: '', + _id: 'bar', _source: { type: 'dashboard', title: 'my saved object title ² with a very long title that exceeds max size bytes', }, }, - { _id: '', _source: { type: 'dashboard', title: 'my saved object title ®' } }, + { _id: 'baz', _source: { type: 'dashboard', title: 'my saved object title ®' } }, ]; - expect(createBatches(documents, INDEX, 178)).toEqual( + expect(createBatches({ documents, maxBatchSizeBytes: 120 })).toEqual( Either.left({ - maxBatchSizeBytes: 178, - docSizeBytes: 179, + maxBatchSizeBytes: 120, + docSizeBytes: 130, type: 'document_exceeds_batch_size_bytes', - document: documents[1], + documentId: documents[1]._id, }) ); }); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/create_batches.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/create_batches.ts index a591505f542c7..ec19f834d9ceb 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/create_batches.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/create_batches.ts @@ -7,27 +7,50 @@ */ import * as Either from 'fp-ts/lib/Either'; -import type { SavedObjectsRawDoc } from '@kbn/core-saved-objects-server'; -import { createBulkOperationBody } from '../actions/bulk_overwrite_transformed_documents'; +import type { SavedObjectsRawDoc, SavedObjectsRawDocSource } from '@kbn/core-saved-objects-server'; +import type { BulkOperationContainer } from '@elastic/elasticsearch/lib/api/types'; +import { createBulkDeleteOperationBody, createBulkIndexOperationTuple } from './helpers'; +import type { TransformErrorObjects } from '../core'; + +export type BulkIndexOperationTuple = [BulkOperationContainer, SavedObjectsRawDocSource]; +export type BulkOperation = BulkIndexOperationTuple | BulkOperationContainer; + +export interface CreateBatchesParams { + documents: SavedObjectsRawDoc[]; + corruptDocumentIds?: string[]; + transformErrors?: TransformErrorObjects[]; + maxBatchSizeBytes: number; +} + +export interface DocumentExceedsBatchSize { + documentId: string; + type: 'document_exceeds_batch_size_bytes'; + docSizeBytes: number; + maxBatchSizeBytes: number; +} /** * Creates batches of documents to be used by the bulk API. Each batch will * have a request body content length that's <= maxBatchSizeBytes */ -export function createBatches( - docs: SavedObjectsRawDoc[], - index: string, - maxBatchSizeBytes: number -) { +export function createBatches({ + documents, + corruptDocumentIds = [], + transformErrors = [], + maxBatchSizeBytes, +}: CreateBatchesParams): Either.Either { /* To build up the NDJSON request body we construct an array of objects like: * [ * {"index": ...} * {"title": "my saved object"} + * {"delete": ...} + * {"delete": ...} * ... * ] - * However, when we call JSON.stringify on this array the resulting string - * will be surrounded by `[]` which won't be present in the NDJSON so these - * two characters need to be removed from the size calculation. + * For indexing operations, createBulkIndexOperationTuple + * returns a tuple of the form [{operation, id}, {document}] + * Thus, for batch size calculations, we must take into account + * that this tuple's surrounding brackets `[]` won't be present in the NDJSON */ const BRACKETS_BYTES = 2; /* Each document in the NDJSON (including the last one) needs to be @@ -36,29 +59,68 @@ export function createBatches( */ const NDJSON_NEW_LINE_BYTES = 1; - const batches = [[]] as [SavedObjectsRawDoc[]]; + const BASE_DELETE_OPERATION_SIZE = Buffer.byteLength( + JSON.stringify(createBulkDeleteOperationBody('')), + 'utf8' + ); + + const batches: BulkOperation[][] = [[]]; let currBatch = 0; let currBatchSizeBytes = 0; - for (const doc of docs) { - const bulkOperationBody = createBulkOperationBody(doc, index); + + // group operations in batches of at most maxBatchSize + const assignToBatch = ( + operation: BulkOperationContainer | BulkIndexOperationTuple, + operationSizeBytes: number + ): boolean => { + operationSizeBytes += NDJSON_NEW_LINE_BYTES; + + if (operationSizeBytes > maxBatchSizeBytes) { + // the current operation (+ payload) does not even fit a single batch, fail! + return false; + } else if (currBatchSizeBytes + operationSizeBytes <= maxBatchSizeBytes) { + batches[currBatch].push(operation); + currBatchSizeBytes = currBatchSizeBytes + operationSizeBytes; + } else { + currBatch++; + batches[currBatch] = [operation]; + currBatchSizeBytes = operationSizeBytes; + } + return true; + }; + + // create index (update) operations for all transformed documents + for (const document of documents) { + const bulkIndexOperationBody = createBulkIndexOperationTuple(document); + // take into account that this tuple's surrounding brackets `[]` won't be present in the NDJSON const docSizeBytes = - Buffer.byteLength(JSON.stringify(bulkOperationBody), 'utf8') - - BRACKETS_BYTES + - NDJSON_NEW_LINE_BYTES; - if (docSizeBytes > maxBatchSizeBytes) { + Buffer.byteLength(JSON.stringify(bulkIndexOperationBody), 'utf8') - BRACKETS_BYTES; + if (!assignToBatch(bulkIndexOperationBody, docSizeBytes)) { return Either.left({ - type: 'document_exceeds_batch_size_bytes', + documentId: document._id, + type: 'document_exceeds_batch_size_bytes' as const, + docSizeBytes, + maxBatchSizeBytes, + }); + } + } + + // create delete operations for all corrupt documents + transform errors + const unwantedDocumentIds = [ + ...corruptDocumentIds, + ...transformErrors.map(({ rawId: documentId }) => documentId), + ]; + + for (const documentId of unwantedDocumentIds) { + const bulkDeleteOperationBody = createBulkDeleteOperationBody(documentId); + const docSizeBytes = BASE_DELETE_OPERATION_SIZE + Buffer.byteLength(documentId, 'utf8'); + if (!assignToBatch(bulkDeleteOperationBody, docSizeBytes)) { + return Either.left({ + documentId, + type: 'document_exceeds_batch_size_bytes' as const, docSizeBytes, maxBatchSizeBytes, - document: doc, }); - } else if (currBatchSizeBytes + docSizeBytes <= maxBatchSizeBytes) { - batches[currBatch].push(doc); - currBatchSizeBytes = currBatchSizeBytes + docSizeBytes; - } else { - currBatch++; - batches[currBatch] = [doc]; - currBatchSizeBytes = docSizeBytes; } } diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/helpers.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/helpers.ts index a7e71bc99e9e0..19ef5d66c0eb5 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/helpers.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/helpers.ts @@ -8,27 +8,29 @@ import { gt, valid } from 'semver'; import type { + BulkOperationContainer, QueryDslBoolQuery, QueryDslQueryContainer, } from '@elastic/elasticsearch/lib/api/types'; import * as Either from 'fp-ts/lib/Either'; +import type { SavedObjectsRawDoc } from '@kbn/core-saved-objects-server'; import type { IndexMapping } from '@kbn/core-saved-objects-base-server-internal'; -import type { State } from '../state'; import type { AliasAction, FetchIndexResponse } from '../actions'; +import type { BulkIndexOperationTuple } from './create_batches'; /** * A helper function/type for ensuring that all control state's are handled. */ export function throwBadControlState(p: never): never; -export function throwBadControlState(controlState: any) { +export function throwBadControlState(controlState: unknown) { throw new Error('Unexpected control state: ' + controlState); } /** * A helper function/type for ensuring that all response types are handled. */ -export function throwBadResponse(state: State, p: never): never; -export function throwBadResponse(state: State, res: any): never { +export function throwBadResponse(state: { controlState: string }, p: never): never; +export function throwBadResponse(state: { controlState: string }, res: unknown): never { throw new Error( `${state.controlState} received unexpected action response: ` + JSON.stringify(res) ); @@ -132,11 +134,11 @@ export function addMustClausesToBoolQuery( /** * Add the given clauses to the 'must_not' of the given query * @param boolQuery the bool query to be enriched - * @param mustNotClauses the clauses to be added to a 'must_not' + * @param filterClauses the clauses to be added to a 'must_not' * @returns a new query container with the enriched query */ export function addMustNotClausesToBoolQuery( - mustNotClauses: QueryDslQueryContainer[], + filterClauses: QueryDslQueryContainer[], boolQuery?: QueryDslBoolQuery ): QueryDslQueryContainer { let mustNot: QueryDslQueryContainer[] = []; @@ -145,7 +147,7 @@ export function addMustNotClausesToBoolQuery( mustNot = mustNot.concat(boolQuery.must_not); } - mustNot.push(...mustNotClauses); + mustNot.push(...filterClauses); return { bool: { @@ -205,3 +207,28 @@ export function buildRemoveAliasActions( return [{ remove: { index, alias, must_exist: true } }]; }); } + +/** + * Given a document, creates a valid body to index the document using the Bulk API. + */ +export const createBulkIndexOperationTuple = (doc: SavedObjectsRawDoc): BulkIndexOperationTuple => { + return [ + { + index: { + _id: doc._id, + // use optimistic concurrency control to ensure that outdated + // documents are only overwritten once with the latest version + ...(typeof doc._seq_no !== 'undefined' && { if_seq_no: doc._seq_no }), + ...(typeof doc._primary_term !== 'undefined' && { if_primary_term: doc._primary_term }), + }, + }, + doc._source, + ]; +}; + +/** + * Given a document id, creates a valid body to delete the document using the Bulk API. + */ +export const createBulkDeleteOperationBody = (_id: string): BulkOperationContainer => ({ + delete: { _id }, +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/model.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/model.test.ts index 3c47ea01ecd72..c07538d1c1184 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/model.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/model.test.ts @@ -11,48 +11,51 @@ import * as Option from 'fp-ts/lib/Option'; import type { SavedObjectsRawDoc } from '@kbn/core-saved-objects-server'; import type { IndexMapping } from '@kbn/core-saved-objects-base-server-internal'; import type { + BaseState, + CalculateExcludeFiltersState, + CheckTargetMappingsState, + CheckUnknownDocumentsState, + CheckVersionIndexReadyActions, + CleanupUnknownAndExcluded, + CleanupUnknownAndExcludedWaitForTaskState, + CloneTempToSource, + CreateNewTargetState, + CreateReindexTempState, FatalState, - State, - LegacySetWriteBlockState, - SetSourceWriteBlockState, LegacyCreateReindexTargetState, + LegacyDeleteState, LegacyReindexState, LegacyReindexWaitForTaskState, - LegacyDeleteState, - ReindexSourceToTempOpenPit, - ReindexSourceToTempRead, - ReindexSourceToTempClosePit, - ReindexSourceToTempTransform, - RefreshTarget, - UpdateTargetMappingsState, - UpdateTargetMappingsWaitForTaskState, + LegacySetWriteBlockState, + MarkVersionIndexReady, + MarkVersionIndexReadyConflict, + OutdatedDocumentsSearchClosePit, OutdatedDocumentsSearchOpenPit, OutdatedDocumentsSearchRead, - OutdatedDocumentsSearchClosePit, OutdatedDocumentsTransform, - MarkVersionIndexReady, - BaseState, - CreateReindexTempState, - MarkVersionIndexReadyConflict, - CreateNewTargetState, - CloneTempToSource, + PostInitState, + PrepareCompatibleMigration, + RefreshTarget, + ReindexSourceToTempClosePit, + ReindexSourceToTempIndexBulk, + ReindexSourceToTempOpenPit, + ReindexSourceToTempRead, + ReindexSourceToTempTransform, + SetSourceWriteBlockState, SetTempWriteBlock, - WaitForYellowSourceState, + State, TransformedDocumentsBulkIndex, - ReindexSourceToTempIndexBulk, - CheckUnknownDocumentsState, - CalculateExcludeFiltersState, - PostInitState, - CheckVersionIndexReadyActions, UpdateTargetMappingsMeta, - CheckTargetMappingsState, - PrepareCompatibleMigration, + UpdateTargetMappingsState, + UpdateTargetMappingsWaitForTaskState, + WaitForYellowSourceState, } from '../state'; import { type TransformErrorObjects, TransformSavedObjectDocumentError } from '../core'; import type { AliasAction, RetryableEsClientError } from '../actions'; import type { ResponseType } from '../next'; import { createInitialProgress } from './progress'; import { model } from './model'; +import type { BulkIndexOperationTuple, BulkOperation } from './create_batches'; describe('migrations v2 model', () => { const indexMapping: IndexMapping = { @@ -112,6 +115,26 @@ describe('migrations v2 model', () => { waitForMigrationCompletion: false, }; + const aProcessedDoc = { + _id: 'a:b', + _source: { type: 'a', a: { name: 'HOI!' }, migrationVersion: {}, references: [] }, + }; + + const processedDocs: SavedObjectsRawDoc[] = [aProcessedDoc]; + + const bulkOperationBatches: BulkOperation[][] = [ + [ + [ + { + index: { + _id: aProcessedDoc._id, + }, + }, + aProcessedDoc._source, + ], + ], + ]; + describe('exponential retry delays for retryable_es_client_error', () => { let state: State = { ...baseState, @@ -1235,16 +1258,7 @@ describe('migrations v2 model', () => { }); describe('and mappings match (diffMappings == false)', () => { - const unchangedMappingsState: State = { - ...waitForYellowSourceState, - controlState: 'WAIT_FOR_YELLOW_SOURCE', - kibanaVersion: '7.12.0', // new version! - currentAlias: '.kibana', - versionAlias: '.kibana_7.12.0', - versionIndex: '.kibana_7.11.0_001', - }; - - test('WAIT_FOR_YELLOW_SOURCE -> PREPARE_COMPATIBLE_MIGRATION', () => { + test('WAIT_FOR_YELLOW_SOURCE -> CLEANUP_UNKNOWN_AND_EXCLUDED', () => { const res: ResponseType<'WAIT_FOR_YELLOW_SOURCE'> = Either.right({ '.kibana_7.11.0_001': { aliases: { @@ -1255,44 +1269,11 @@ describe('migrations v2 model', () => { settings: {}, }, }); - const newState = model(unchangedMappingsState, res) as PrepareCompatibleMigration; + const newState = model(waitForYellowSourceState, res) as CleanupUnknownAndExcluded; - expect(newState.controlState).toEqual('PREPARE_COMPATIBLE_MIGRATION'); - expect(newState.targetIndexRawMappings).toEqual({ - _meta: { - migrationMappingPropertyHashes: { - new_saved_object_type: '4a11183eee21e6fbad864f7a30b39ad0', - }, - }, - properties: { - new_saved_object_type: { - properties: { - value: { - type: 'text', - }, - }, - }, - }, - }); - expect(newState.versionAlias).toEqual('.kibana_7.12.0'); - expect(newState.currentAlias).toEqual('.kibana'); - // will point to - expect(newState.targetIndex).toEqual('.kibana_7.11.0_001'); - expect(newState.preTransformDocsActions).toEqual([ - { - add: { - alias: '.kibana_7.12.0', - index: '.kibana_7.11.0_001', - }, - }, - { - remove: { - alias: '.kibana_7.11.0', - index: '.kibana_7.11.0_001', - must_exist: true, - }, - }, - ]); + expect(newState.controlState).toEqual('CLEANUP_UNKNOWN_AND_EXCLUDED'); + expect(newState.targetIndex).toEqual(baseState.versionIndex); + expect(newState.versionIndexReadyActions).toEqual(Option.none); }); }); @@ -1312,13 +1293,8 @@ describe('migrations v2 model', () => { }, }; - const changedMappingsState: State = { + const changedMappingsState: WaitForYellowSourceState = { ...waitForYellowSourceState, - controlState: 'WAIT_FOR_YELLOW_SOURCE', - kibanaVersion: '7.12.0', // new version! - currentAlias: '.kibana', - versionAlias: '.kibana_7.12.0', - versionIndex: '.kibana_7.11.0_001', sourceIndexMappings: actualMappings, }; @@ -1354,6 +1330,178 @@ describe('migrations v2 model', () => { }); }); + describe('CLEANUP_UNKNOWN_AND_EXCLUDED', () => { + const cleanupUnknownAndExcluded: CleanupUnknownAndExcluded = { + ...baseState, + controlState: 'CLEANUP_UNKNOWN_AND_EXCLUDED', + sourceIndex: Option.some('.kibana_7.11.0_001') as Option.Some, + sourceIndexMappings: baseState.targetIndexMappings, + targetIndex: baseState.versionIndex, + kibanaVersion: '7.12.0', // new version! + currentAlias: '.kibana', + versionAlias: '.kibana_7.12.0', + aliases: { + '.kibana': '.kibana_7.11.0_001', + '.kibana_7.11.0': '.kibana_7.11.0_001', + }, + versionIndexReadyActions: Option.none, + }; + + describe('if action succeeds', () => { + test('CLEANUP_UNKNOWN_AND_EXCLUDED -> CLEANUP_UNKNOWN_AND_EXCLUDED_WAIT_FOR_TASK', () => { + const res: ResponseType<'CLEANUP_UNKNOWN_AND_EXCLUDED'> = Either.right({ + type: 'cleanup_started' as const, + taskId: '1234', + unknownDocs: [], + errorsByType: {}, + }); + const newState = model(cleanupUnknownAndExcluded, res) as PrepareCompatibleMigration; + + expect(newState.controlState).toEqual('CLEANUP_UNKNOWN_AND_EXCLUDED_WAIT_FOR_TASK'); + // expect(newState.targetIndexRawMappings).toEqual(indexMapping); + // expect(newState.targetIndexMappings).toEqual(indexMapping); + // expect(newState.targetIndex).toEqual('.kibana_7.11.0_001'); + // expect(newState.preTransformDocsActions).toEqual([ + // { + // add: { + // alias: '.kibana_7.12.0', + // index: '.kibana_7.11.0_001', + // }, + // }, + // { + // remove: { + // alias: '.kibana_7.11.0', + // index: '.kibana_7.11.0_001', + // must_exist: true, + // }, + // }, + // ]); + }); + }); + + test('CLEANUP_UNKNOWN_AND_EXCLUDED -> FATAL if discardUnknownObjects=false', () => { + const res: ResponseType<'CLEANUP_UNKNOWN_AND_EXCLUDED'> = Either.left({ + type: 'unknown_docs_found' as const, + unknownDocs: [ + { id: 'dashboard:12', type: 'dashboard' }, + { id: 'foo:17', type: 'foo' }, + ], + }); + + const newState = model(cleanupUnknownAndExcluded, res); + + expect(newState).toMatchObject({ + controlState: 'FATAL', + reason: expect.stringContaining( + 'Migration failed because some documents were found which use unknown saved object types' + ), + }); + }); + }); + + describe('CLEANUP_UNKNOWN_AND_EXCLUDED_WAIT_FOR_TASK', () => { + const cleanupUnknownAndExcludedWaitForTask: CleanupUnknownAndExcludedWaitForTaskState = { + ...baseState, + controlState: 'CLEANUP_UNKNOWN_AND_EXCLUDED_WAIT_FOR_TASK', + deleteByQueryTaskId: '1234', + sourceIndex: Option.some('.kibana_7.11.0_001') as Option.Some, + sourceIndexMappings: baseState.targetIndexMappings, + targetIndex: baseState.versionIndex, + kibanaVersion: '7.12.0', // new version! + currentAlias: '.kibana', + versionAlias: '.kibana_7.12.0', + aliases: { + '.kibana': '.kibana_7.11.0_001', + '.kibana_7.11.0': '.kibana_7.11.0_001', + }, + versionIndexReadyActions: Option.none, + }; + + test('CLEANUP_UNKNOWN_AND_EXCLUDED_WAIT_FOR_TASK -> CLEANUP_UNKNOWN_AND_EXCLUDED_WAIT_FOR_TASK when response is left wait_for_task_completion_timeout', () => { + const res: ResponseType<'UPDATE_TARGET_MAPPINGS_WAIT_FOR_TASK'> = Either.left({ + message: '[timeout_exception] Timeout waiting for ...', + type: 'wait_for_task_completion_timeout', + }); + const newState = model(cleanupUnknownAndExcludedWaitForTask, res); + expect(newState.controlState).toEqual('CLEANUP_UNKNOWN_AND_EXCLUDED_WAIT_FOR_TASK'); + expect(newState.retryCount).toEqual(1); + expect(newState.retryDelay).toEqual(2000); + }); + + test('CLEANUP_UNKNOWN_AND_EXCLUDED_WAIT_FOR_TASK -> PREPARE_COMPATIBLE_MIGRATION if action succeeds', () => { + const res: ResponseType<'CLEANUP_UNKNOWN_AND_EXCLUDED_WAIT_FOR_TASK'> = Either.right({ + type: 'cleanup_successful' as const, + }); + const newState = model( + cleanupUnknownAndExcludedWaitForTask, + res + ) as PrepareCompatibleMigration; + + expect(newState.controlState).toEqual('PREPARE_COMPATIBLE_MIGRATION'); + expect(newState.targetIndexRawMappings).toEqual(indexMapping); + expect(newState.targetIndexMappings).toEqual(indexMapping); + expect(newState.targetIndex).toEqual('.kibana_7.11.0_001'); + expect(newState.preTransformDocsActions).toEqual([ + { + add: { + alias: '.kibana_7.12.0', + index: '.kibana_7.11.0_001', + }, + }, + { + remove: { + alias: '.kibana_7.11.0', + index: '.kibana_7.11.0_001', + must_exist: true, + }, + }, + ]); + }); + + test('CLEANUP_UNKNOWN_AND_EXCLUDED_WAIT_FOR_TASK -> CLEANUP_UNKNOWN_AND_EXCLUDED if the deleteQuery fails and we have some attempts left', () => { + const res: ResponseType<'CLEANUP_UNKNOWN_AND_EXCLUDED_WAIT_FOR_TASK'> = Either.left({ + type: 'cleanup_failed' as const, + failures: ['Failed to delete dashboard:12345', 'Failed to delete dashboard:67890'], + versionConflicts: 12, + }); + + const newState = model(cleanupUnknownAndExcludedWaitForTask, res); + + expect(newState).toMatchObject({ + controlState: 'CLEANUP_UNKNOWN_AND_EXCLUDED', + logs: [ + { + level: 'warning', + message: + 'Errors occurred whilst deleting unwanted documents. Another instance is probably updating or deleting documents in the same index. Retrying attempt 1.', + }, + ], + }); + }); + + test('CLEANUP_UNKNOWN_AND_EXCLUDED_WAIT_FOR_TASK -> FAIL if the deleteQuery fails after N retries', () => { + const res: ResponseType<'CLEANUP_UNKNOWN_AND_EXCLUDED_WAIT_FOR_TASK'> = Either.left({ + type: 'cleanup_failed' as const, + failures: ['Failed to delete dashboard:12345', 'Failed to delete dashboard:67890'], + }); + + const newState = model( + { + ...cleanupUnknownAndExcludedWaitForTask, + retryCount: cleanupUnknownAndExcludedWaitForTask.retryAttempts, + }, + res + ); + + expect(newState).toMatchObject({ + controlState: 'FATAL', + reason: expect.stringContaining( + 'Migration failed because it was unable to delete unwanted documents from the .kibana_7.11.0_001 system index' + ), + }); + }); + }); + describe('CHECK_UNKNOWN_DOCUMENTS', () => { const mappingsWithUnknownType = { properties: { @@ -1536,7 +1684,7 @@ describe('migrations v2 model', () => { }); it('CALCULATE_EXCLUDE_FILTERS -> CREATE_REINDEX_TEMP if action succeeds with filters', () => { const res: ResponseType<'CALCULATE_EXCLUDE_FILTERS'> = Either.right({ - mustNotClauses: [{ term: { fieldA: 'abc' } }], + filterClauses: [{ term: { fieldA: 'abc' } }], errorsByType: { type1: new Error('an error!') }, }); const newState = model(state, res); @@ -1784,12 +1932,6 @@ describe('migrations v2 model', () => { transformErrors: [], progress: { processed: undefined, total: 1 }, }; - const processedDocs = [ - { - _id: 'a:b', - _source: { type: 'a', a: { name: 'HOI!' }, migrationVersion: {}, references: [] }, - }, - ] as SavedObjectsRawDoc[]; it('REINDEX_SOURCE_TO_TEMP_TRANSFORM -> REINDEX_SOURCE_TO_TEMP_INDEX_BULK if action succeeded', () => { const res: ResponseType<'REINDEX_SOURCE_TO_TEMP_TRANSFORM'> = Either.right({ @@ -1798,7 +1940,7 @@ describe('migrations v2 model', () => { const newState = model(state, res) as ReindexSourceToTempIndexBulk; expect(newState.controlState).toEqual('REINDEX_SOURCE_TO_TEMP_INDEX_BULK'); expect(newState.currentBatch).toEqual(0); - expect(newState.transformedDocBatches).toEqual([processedDocs]); + expect(newState.bulkOperationBatches).toEqual(bulkOperationBatches); expect(newState.progress.processed).toBe(0); // Result of `(undefined ?? 0) + corruptDocumentsId.length` }); @@ -1854,18 +1996,10 @@ describe('migrations v2 model', () => { }); describe('REINDEX_SOURCE_TO_TEMP_INDEX_BULK', () => { - const transformedDocBatches = [ - [ - { - _id: 'a:b', - _source: { type: 'a', a: { name: 'HOI!' }, migrationVersion: {}, references: [] }, - }, - ], - ] as [SavedObjectsRawDoc[]]; const reindexSourceToTempIndexBulkState: ReindexSourceToTempIndexBulk = { ...baseState, controlState: 'REINDEX_SOURCE_TO_TEMP_INDEX_BULK', - transformedDocBatches, + bulkOperationBatches, currentBatch: 0, versionIndexReadyActions: Option.none, sourceIndex: Option.some('.kibana') as Option.Some, @@ -2024,6 +2158,18 @@ describe('migrations v2 model', () => { preTransformDocsActions: [someAliasAction], }; + it('PREPARE_COMPATIBLE_MIGRATIONS -> REFRESH_TARGET if action succeeds and we must refresh the index', () => { + const res: ResponseType<'PREPARE_COMPATIBLE_MIGRATION'> = Either.right( + 'update_aliases_succeeded' + ); + const newState = model( + { ...state, mustRefresh: true }, + res + ) as OutdatedDocumentsSearchOpenPit; + expect(newState.controlState).toEqual('REFRESH_TARGET'); + expect(newState.versionIndexReadyActions).toEqual(Option.none); + }); + it('PREPARE_COMPATIBLE_MIGRATIONS -> OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT if action succeeds', () => { const res: ResponseType<'PREPARE_COMPATIBLE_MIGRATION'> = Either.right( 'update_aliases_succeeded' @@ -2033,6 +2179,19 @@ describe('migrations v2 model', () => { expect(newState.versionIndexReadyActions).toEqual(Option.none); }); + it('PREPARE_COMPATIBLE_MIGRATIONS -> REFRESH_TARGET if action fails because the alias is not found', () => { + const res: ResponseType<'PREPARE_COMPATIBLE_MIGRATION'> = Either.left({ + type: 'alias_not_found_exception', + }); + + const newState = model( + { ...state, mustRefresh: true }, + res + ) as OutdatedDocumentsSearchOpenPit; + expect(newState.controlState).toEqual('REFRESH_TARGET'); + expect(newState.versionIndexReadyActions).toEqual(Option.none); + }); + it('PREPARE_COMPATIBLE_MIGRATIONS -> OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT if action fails because the alias is not found', () => { const res: ResponseType<'PREPARE_COMPATIBLE_MIGRATION'> = Either.left({ type: 'alias_not_found_exception', @@ -2268,12 +2427,6 @@ describe('migrations v2 model', () => { progress: createInitialProgress(), }; describe('OUTDATED_DOCUMENTS_TRANSFORM if action succeeds', () => { - const processedDocs = [ - { - _id: 'a:b', - _source: { type: 'a', a: { name: 'HOI!' }, migrationVersion: {}, references: [] }, - }, - ] as SavedObjectsRawDoc[]; test('OUTDATED_DOCUMENTS_TRANSFORM -> TRANSFORMED_DOCUMENTS_BULK_INDEX if action succeeds', () => { const res: ResponseType<'OUTDATED_DOCUMENTS_TRANSFORM'> = Either.right({ processedDocs }); const newState = model( @@ -2281,7 +2434,7 @@ describe('migrations v2 model', () => { res ) as TransformedDocumentsBulkIndex; expect(newState.controlState).toEqual('TRANSFORMED_DOCUMENTS_BULK_INDEX'); - expect(newState.transformedDocBatches).toEqual([processedDocs]); + expect(newState.bulkOperationBatches).toEqual(bulkOperationBatches); expect(newState.currentBatch).toEqual(0); expect(newState.retryCount).toEqual(0); expect(newState.retryDelay).toEqual(0); @@ -2367,30 +2520,28 @@ describe('migrations v2 model', () => { }); describe('TRANSFORMED_DOCUMENTS_BULK_INDEX', () => { - const transformedDocBatches = [ - [ - // batch 0 - { - _id: 'a:b', - _source: { type: 'a', a: { name: 'HOI!' }, migrationVersion: {}, references: [] }, - }, - { - _id: 'a:c', - _source: { type: 'a', a: { name: 'HOI!' }, migrationVersion: {}, references: [] }, - }, - ], - [ - // batch 1 - { - _id: 'a:d', - _source: { type: 'a', a: { name: 'HOI!' }, migrationVersion: {}, references: [] }, + const idToIndexOperation = (_id: string): BulkIndexOperationTuple => [ + // "index" operations have a first part with the operation and the SO id + { + index: { + _id, }, - ], - ] as SavedObjectsRawDoc[][]; + }, + // and a second part with the object _source + { type: 'a', a: { name: `HOI ${_id}!` }, migrationVersion: {}, references: [] }, + // these two parts are then serialized to NDJSON by esClient and sent over with POST _bulk + ]; + + const customBulkOperationBatches: BulkOperation[][] = [ + // batch 0 + ['a:b', 'a:c'].map(idToIndexOperation), + // batch 1 + ['a:d'].map(idToIndexOperation), + ]; const transformedDocumentsBulkIndexState: TransformedDocumentsBulkIndex = { ...baseState, controlState: 'TRANSFORMED_DOCUMENTS_BULK_INDEX', - transformedDocBatches, + bulkOperationBatches: customBulkOperationBatches, currentBatch: 0, versionIndexReadyActions: Option.none, sourceIndex: Option.some('.kibana') as Option.Some, diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/model.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/model.ts index 167a5d3771b78..4c2a9147eb125 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/model.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/model.ts @@ -9,7 +9,8 @@ import * as Either from 'fp-ts/lib/Either'; import * as Option from 'fp-ts/lib/Option'; -import { type AliasAction, isTypeof } from '../actions'; +import { isTypeof } from '../actions'; +import type { AliasAction } from '../actions'; import type { AllActionStates, State } from '../state'; import type { ResponseType } from '../next'; import { @@ -278,30 +279,6 @@ export const model = (currentState: State, resW: ResponseType): ], }; } - } else if (stateP.controlState === 'PREPARE_COMPATIBLE_MIGRATION') { - const res = resW as ExcludeRetryableEsError>; - if (Either.isRight(res)) { - return { - ...stateP, - controlState: 'OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT', - }; - } else if (Either.isLeft(res)) { - // Note: if multiple newer Kibana versions are competing with each other to perform a migration, - // it might happen that another Kibana instance has deleted this instance's version index. - // NIT to handle this in properly, we'd have to add a PREPARE_COMPATIBLE_MIGRATION_CONFLICT step, - // similar to MARK_VERSION_INDEX_READY_CONFLICT. - if (isTypeof(res.left, 'alias_not_found_exception')) { - // We assume that the alias was already deleted by another Kibana instance - return { - ...stateP, - controlState: 'OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT', - }; - } else { - throwBadResponse(stateP, res.left as never); - } - } else { - throwBadResponse(stateP, res); - } } else if (stateP.controlState === 'LEGACY_SET_WRITE_BLOCK') { const res = resW as ExcludeRetryableEsError>; // If the write block is successfully in place @@ -457,35 +434,16 @@ export const model = (currentState: State, resW: ResponseType): stateP.sourceIndexMappings, /* expected */ stateP.targetIndexMappings - ) + ) && + Math.random() < 10 ) { - // The source index .kibana is pointing to. E.g: ".xx8.7.0_001" - const source = stateP.sourceIndex.value; - return { ...stateP, - controlState: 'PREPARE_COMPATIBLE_MIGRATION', - sourceIndex: Option.none, - targetIndex: source!, - targetIndexRawMappings: stateP.sourceIndexMappings, - targetIndexMappings: mergeMigrationMappingPropertyHashes( - stateP.targetIndexMappings, - stateP.sourceIndexMappings - ), - preTransformDocsActions: [ - // Point the version alias to the source index. This let's other Kibana - // instances know that a migration for the current version is "done" - // even though we may be waiting for document transformations to finish. - { add: { index: source!, alias: stateP.versionAlias } }, - ...buildRemoveAliasActions(source!, Object.keys(stateP.aliases), [ - stateP.currentAlias, - stateP.versionAlias, - ]), - ], + controlState: 'CLEANUP_UNKNOWN_AND_EXCLUDED', + targetIndex: stateP.sourceIndex.value!, // We preserve the same index, source == target (E.g: ".xx8.7.0_001") versionIndexReadyActions: Option.none, }; } else { - // the mappings have changed, but changes might still be compatible return { ...stateP, controlState: 'CHECK_UNKNOWN_DOCUMENTS', @@ -507,6 +465,133 @@ export const model = (currentState: State, resW: ResponseType): } else { return throwBadResponse(stateP, res); } + } else if (stateP.controlState === 'CLEANUP_UNKNOWN_AND_EXCLUDED') { + const res = resW as ExcludeRetryableEsError>; + if (Either.isRight(res)) { + if (res.right.unknownDocs.length) { + logs = [ + ...stateP.logs, + { level: 'warning', message: extractDiscardedUnknownDocs(res.right.unknownDocs) }, + ]; + } + + logs = [ + ...logs, + ...Object.entries(res.right.errorsByType).map(([soType, error]) => ({ + level: 'warning' as const, + message: `Ignored excludeOnUpgrade hook on type [${soType}] that failed with error: "${error.toString()}"`, + })), + ]; + + return { + ...stateP, + logs, + controlState: 'CLEANUP_UNKNOWN_AND_EXCLUDED_WAIT_FOR_TASK', + deleteByQueryTaskId: res.right.taskId, + }; + } else { + return { + ...stateP, + controlState: 'FATAL', + reason: extractUnknownDocFailureReason( + stateP.migrationDocLinks.resolveMigrationFailures, + res.left.unknownDocs + ), + }; + } + } else if (stateP.controlState === 'CLEANUP_UNKNOWN_AND_EXCLUDED_WAIT_FOR_TASK') { + const res = resW as ExcludeRetryableEsError>; + if (Either.isRight(res)) { + const source = stateP.sourceIndex.value; + return { + ...stateP, + logs, + controlState: 'PREPARE_COMPATIBLE_MIGRATION', + mustRefresh: + stateP.mustRefresh || typeof res.right.deleted === 'undefined' || res.right.deleted > 0, + targetIndexRawMappings: stateP.sourceIndexMappings, + targetIndexMappings: mergeMigrationMappingPropertyHashes( + stateP.targetIndexMappings, + stateP.sourceIndexMappings + ), + preTransformDocsActions: [ + // Point the version alias to the source index. This let's other Kibana + // instances know that a migration for the current version is "done" + // even though we may be waiting for document transformations to finish. + { add: { index: source!, alias: stateP.versionAlias } }, + ...buildRemoveAliasActions(source!, Object.keys(stateP.aliases), [ + stateP.currentAlias, + stateP.versionAlias, + ]), + ], + }; + } else { + if (isTypeof(res.left, 'wait_for_task_completion_timeout')) { + // After waiting for the specified timeout, the task has not yet + // completed. Retry this step to see if the task has completed after an + // exponential delay. We will basically keep polling forever until the + // Elasticsearch task succeeds or fails. + return delayRetryState(stateP, res.left.message, Number.MAX_SAFE_INTEGER); + } else { + if (stateP.retryCount < stateP.retryAttempts) { + const retryCount = stateP.retryCount + 1; + const retryDelay = 1500 + 1000 * Math.random(); + return { + ...stateP, + controlState: 'CLEANUP_UNKNOWN_AND_EXCLUDED', + mustRefresh: true, + retryCount, + retryDelay, + logs: [ + ...stateP.logs, + { + level: 'warning', + message: `Errors occurred whilst deleting unwanted documents. Another instance is probably updating or deleting documents in the same index. Retrying attempt ${retryCount}.`, + }, + ], + }; + } else { + const failures = res.left.failures.length; + const versionConflicts = res.left.versionConflicts ?? 0; + + let reason = `Migration failed because it was unable to delete unwanted documents from the ${stateP.sourceIndex.value} system index (${failures} failures and ${versionConflicts} conflicts)`; + if (failures) { + reason += `:\n` + res.left.failures.map((failure: string) => `- ${failure}\n`).join(''); + } + return { + ...stateP, + controlState: 'FATAL', + reason, + }; + } + } + } + } else if (stateP.controlState === 'PREPARE_COMPATIBLE_MIGRATION') { + const res = resW as ExcludeRetryableEsError>; + if (Either.isRight(res)) { + return { + ...stateP, + controlState: stateP.mustRefresh ? 'REFRESH_TARGET' : 'OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT', + }; + } else if (Either.isLeft(res)) { + // Note: if multiple newer Kibana versions are competing with each other to perform a migration, + // it might happen that another Kibana instance has deleted this instance's version index. + // NIT to handle this in properly, we'd have to add a PREPARE_COMPATIBLE_MIGRATION_CONFLICT step, + // similar to MARK_VERSION_INDEX_READY_CONFLICT. + if (isTypeof(res.left, 'alias_not_found_exception')) { + // We assume that the alias was already deleted by another Kibana instance + return { + ...stateP, + controlState: stateP.mustRefresh + ? 'REFRESH_TARGET' + : 'OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT', + }; + } else { + throwBadResponse(stateP, res.left as never); + } + } else { + throwBadResponse(stateP, res); + } } else if (stateP.controlState === 'CHECK_UNKNOWN_DOCUMENTS') { const res = resW as ExcludeRetryableEsError>; @@ -579,7 +664,7 @@ export const model = (currentState: State, resW: ResponseType): if (Either.isRight(res)) { excludeOnUpgradeQuery = addMustNotClausesToBoolQuery( - res.right.mustNotClauses, + res.right.filterClauses, stateP.excludeOnUpgradeQuery?.bool ); @@ -733,10 +818,8 @@ export const model = (currentState: State, resW: ResponseType): (stateP.corruptDocumentIds.length === 0 && stateP.transformErrors.length === 0) || stateP.discardCorruptObjects ) { - const processedDocs = Either.isRight(res) - ? res.right.processedDocs - : res.left.processedDocs; - const batches = createBatches(processedDocs, stateP.tempIndex, stateP.maxBatchSizeBytes); + const documents = Either.isRight(res) ? res.right.processedDocs : res.left.processedDocs; + const batches = createBatches({ documents, maxBatchSizeBytes: stateP.maxBatchSizeBytes }); if (Either.isRight(batches)) { let corruptDocumentIds = stateP.corruptDocumentIds; let transformErrors = stateP.transformErrors; @@ -751,7 +834,7 @@ export const model = (currentState: State, resW: ResponseType): corruptDocumentIds, transformErrors, controlState: 'REINDEX_SOURCE_TO_TEMP_INDEX_BULK', // handles the actual bulk indexing into temp index - transformedDocBatches: batches.right, + bulkOperationBatches: batches.right, currentBatch: 0, progress, }; @@ -760,7 +843,7 @@ export const model = (currentState: State, resW: ResponseType): ...stateP, controlState: 'FATAL', reason: fatalReasonDocumentExceedsMaxBatchSizeBytes({ - _id: batches.left.document._id, + _id: batches.left.documentId, docSizeBytes: batches.left.docSizeBytes, maxBatchSizeBytes: batches.left.maxBatchSizeBytes, }), @@ -796,7 +879,7 @@ export const model = (currentState: State, resW: ResponseType): } else if (stateP.controlState === 'REINDEX_SOURCE_TO_TEMP_INDEX_BULK') { const res = resW as ExcludeRetryableEsError>; if (Either.isRight(res)) { - if (stateP.currentBatch + 1 < stateP.transformedDocBatches.length) { + if (stateP.currentBatch + 1 < stateP.bulkOperationBatches.length) { return { ...stateP, controlState: 'REINDEX_SOURCE_TO_TEMP_INDEX_BULK', @@ -938,25 +1021,40 @@ export const model = (currentState: State, resW: ResponseType): } else { // we don't have any more outdated documents and need to either fail or move on to updating the target mappings. if (stateP.corruptDocumentIds.length > 0 || stateP.transformErrors.length > 0) { - const transformFailureReason = extractTransformFailuresReason( - stateP.migrationDocLinks.resolveMigrationFailures, - stateP.corruptDocumentIds, - stateP.transformErrors - ); - return { - ...stateP, - controlState: 'FATAL', - reason: transformFailureReason, - }; - } else { - // If there are no more results we have transformed all outdated - // documents and we didn't encounter any corrupt documents or transformation errors - // and can proceed to the next step - return { - ...stateP, - controlState: 'OUTDATED_DOCUMENTS_SEARCH_CLOSE_PIT', - }; + if (!stateP.discardCorruptObjects) { + const transformFailureReason = extractTransformFailuresReason( + stateP.migrationDocLinks.resolveMigrationFailures, + stateP.corruptDocumentIds, + stateP.transformErrors + ); + return { + ...stateP, + controlState: 'FATAL', + reason: transformFailureReason, + }; + } + + // at this point, users have configured kibana to discard corrupt objects + // thus, we can ignore corrupt documents and transform errors and proceed with the migration + logs = [ + ...stateP.logs, + { + level: 'warning', + message: extractDiscardedCorruptDocs( + stateP.corruptDocumentIds, + stateP.transformErrors + ), + }, + ]; } + + // If there are no more results we have transformed all outdated + // documents and we didn't encounter any corrupt documents or transformation errors + // and can proceed to the next step + return { + ...stateP, + controlState: 'OUTDATED_DOCUMENTS_SEARCH_CLOSE_PIT', + }; } } else { throwBadResponse(stateP, res); @@ -968,20 +1066,36 @@ export const model = (currentState: State, resW: ResponseType): // Otherwise the progress might look off when there are errors. const progress = incrementProcessedProgress(stateP.progress, stateP.outdatedDocuments.length); - if (Either.isRight(res)) { - // we haven't seen corrupt documents or any transformation errors thus far in the migration - // index the migrated docs - if (stateP.corruptDocumentIds.length === 0 && stateP.transformErrors.length === 0) { - const batches = createBatches( - res.right.processedDocs, - stateP.targetIndex, - stateP.maxBatchSizeBytes - ); + if ( + Either.isRight(res) || + (isTypeof(res.left, 'documents_transform_failed') && stateP.discardCorruptObjects) + ) { + // we might have some transformation errors, but user has chosen to discard them + if ( + (stateP.corruptDocumentIds.length === 0 && stateP.transformErrors.length === 0) || + stateP.discardCorruptObjects + ) { + const documents = Either.isRight(res) ? res.right.processedDocs : res.left.processedDocs; + + let corruptDocumentIds = stateP.corruptDocumentIds; + let transformErrors = stateP.transformErrors; + + if (Either.isLeft(res)) { + corruptDocumentIds = [...stateP.corruptDocumentIds, ...res.left.corruptDocumentIds]; + transformErrors = [...stateP.transformErrors, ...res.left.transformErrors]; + } + + const batches = createBatches({ + documents, + corruptDocumentIds, + transformErrors, + maxBatchSizeBytes: stateP.maxBatchSizeBytes, + }); if (Either.isRight(batches)) { return { ...stateP, controlState: 'TRANSFORMED_DOCUMENTS_BULK_INDEX', - transformedDocBatches: batches.right, + bulkOperationBatches: batches.right, currentBatch: 0, hasTransformedDocs: true, progress, @@ -991,7 +1105,7 @@ export const model = (currentState: State, resW: ResponseType): ...stateP, controlState: 'FATAL', reason: fatalReasonDocumentExceedsMaxBatchSizeBytes({ - _id: batches.left.document._id, + _id: batches.left.documentId, docSizeBytes: batches.left.docSizeBytes, maxBatchSizeBytes: batches.left.maxBatchSizeBytes, }), @@ -1024,7 +1138,7 @@ export const model = (currentState: State, resW: ResponseType): } else if (stateP.controlState === 'TRANSFORMED_DOCUMENTS_BULK_INDEX') { const res = resW as ExcludeRetryableEsError>; if (Either.isRight(res)) { - if (stateP.currentBatch + 1 < stateP.transformedDocBatches.length) { + if (stateP.currentBatch + 1 < stateP.bulkOperationBatches.length) { return { ...stateP, controlState: 'TRANSFORMED_DOCUMENTS_BULK_INDEX', diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/retry_state.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/retry_state.ts index 532cc70347916..f6fb8bf8a1904 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/retry_state.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/retry_state.ts @@ -6,9 +6,16 @@ * Side Public License, v 1. */ -import { State } from '../state'; +import type { MigrationLog } from '../types'; -export const delayRetryState = ( +export interface RetryableState { + controlState: string; + retryCount: number; + retryDelay: number; + logs: MigrationLog[]; +} + +export const delayRetryState = ( state: S, errorMessage: string, /** How many times to retry a step that fails */ @@ -39,7 +46,7 @@ export const delayRetryState = ( }; } }; -export const resetRetryState = (state: S): S => { +export const resetRetryState = (state: S): S => { return { ...state, retryCount: 0, diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/next.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/next.ts index 386786baf60c8..605dd149855e7 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/next.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/next.ts @@ -43,9 +43,12 @@ import type { WaitForMigrationCompletionState, CheckTargetMappingsState, PrepareCompatibleMigration, + CleanupUnknownAndExcluded, + CleanupUnknownAndExcludedWaitForTaskState, } from './state'; import type { TransformRawDocs } from './types'; import * as Actions from './actions'; +import { REMOVED_TYPES } from './core'; type ActionMap = ReturnType; @@ -63,12 +66,30 @@ export const nextActionMap = (client: ElasticsearchClient, transformRawDocs: Tra return { INIT: (state: InitState) => Actions.initAction({ client, indices: [state.currentAlias, state.versionAlias] }), - PREPARE_COMPATIBLE_MIGRATION: (state: PrepareCompatibleMigration) => - Actions.updateAliases({ client, aliasActions: state.preTransformDocsActions }), WAIT_FOR_MIGRATION_COMPLETION: (state: WaitForMigrationCompletionState) => Actions.fetchIndices({ client, indices: [state.currentAlias, state.versionAlias] }), WAIT_FOR_YELLOW_SOURCE: (state: WaitForYellowSourceState) => Actions.waitForIndexStatus({ client, index: state.sourceIndex.value, status: 'yellow' }), + CLEANUP_UNKNOWN_AND_EXCLUDED: (state: CleanupUnknownAndExcluded) => + Actions.cleanupUnknownAndExcluded({ + client, + indexName: state.sourceIndex.value, + discardUnknownDocs: state.discardUnknownObjects, + excludeOnUpgradeQuery: state.excludeOnUpgradeQuery, + excludeFromUpgradeFilterHooks: state.excludeFromUpgradeFilterHooks, + knownTypes: state.knownTypes, + removedTypes: REMOVED_TYPES, + }), + CLEANUP_UNKNOWN_AND_EXCLUDED_WAIT_FOR_TASK: ( + state: CleanupUnknownAndExcludedWaitForTaskState + ) => + Actions.waitForDeleteByQueryTask({ + client, + taskId: state.deleteByQueryTaskId, + timeout: '120s', + }), + PREPARE_COMPATIBLE_MIGRATION: (state: PrepareCompatibleMigration) => + Actions.updateAliases({ client, aliasActions: state.preTransformDocsActions }), CHECK_UNKNOWN_DOCUMENTS: (state: CheckUnknownDocumentsState) => Actions.checkForUnknownDocs({ client, @@ -117,7 +138,7 @@ export const nextActionMap = (client: ElasticsearchClient, transformRawDocs: Tra Actions.bulkOverwriteTransformedDocuments({ client, index: state.tempIndex, - transformedDocs: state.transformedDocBatches[state.currentBatch], + operations: state.bulkOperationBatches[state.currentBatch], /** * Since we don't run a search against the target index, we disable "refresh" to speed up * the migration process. @@ -178,14 +199,14 @@ export const nextActionMap = (client: ElasticsearchClient, transformRawDocs: Tra Actions.bulkOverwriteTransformedDocuments({ client, index: state.targetIndex, - transformedDocs: state.transformedDocBatches[state.currentBatch], + operations: state.bulkOperationBatches[state.currentBatch], /** * Since we don't run a search against the target index, we disable "refresh" to speed up * the migration process. * Although any further step must run "refresh" for the target index - * before we reach out to the MARK_VERSION_INDEX_READY step. * Right now, it's performed during OUTDATED_DOCUMENTS_REFRESH step. */ + refresh: false, }), MARK_VERSION_INDEX_READY: (state: MarkVersionIndexReady) => Actions.updateAliases({ client, aliasActions: state.versionIndexReadyActions.value }), diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/state.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/state.ts index f3a59fadf2dd4..a091a2972343f 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/state.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/state.ts @@ -18,6 +18,7 @@ import type { ControlState } from './state_action_machine'; import type { AliasAction } from './actions'; import type { TransformErrorObjects } from './core'; import type { MigrationLog, Progress } from './types'; +import type { BulkOperation } from './model/create_batches'; export interface BaseState extends ControlState { /** The first part of the index name such as `.kibana` or `.kibana_task_manager` */ @@ -180,14 +181,37 @@ export interface PostInitState extends BaseState { */ readonly targetIndexRawMappings?: IndexMapping; readonly versionIndexReadyActions: Option.Option; - readonly outdatedDocumentsQuery: QueryDslQueryContainer; } +export interface SourceExistsState { + readonly sourceIndex: Option.Some; +} +export type BaseWithSource = BaseState & SourceExistsState; +export type PostInitWithSource = PostInitState & SourceExistsState; + export interface DoneState extends PostInitState { /** Migration completed successfully */ readonly controlState: 'DONE'; } +export interface CleanupUnknownAndExcluded extends PostInitWithSource { + /** Clean the source index, removing SOs with unknown and excluded types */ + readonly controlState: 'CLEANUP_UNKNOWN_AND_EXCLUDED'; + readonly sourceIndexMappings: IndexMapping; + readonly aliases: Record; + /** The cleanup operation has deleted one or more documents, we gotta refresh the index */ + readonly mustRefresh?: boolean; +} + +export interface CleanupUnknownAndExcludedWaitForTaskState extends PostInitWithSource { + readonly controlState: 'CLEANUP_UNKNOWN_AND_EXCLUDED_WAIT_FOR_TASK'; + readonly deleteByQueryTaskId: string; + readonly sourceIndexMappings: IndexMapping; + readonly aliases: Record; + /** The cleanup operation has deleted one or more documents, we gotta refresh the index */ + readonly mustRefresh?: boolean; +} + /** * Compatibe migrations do not require migrating to a new index because all * schema changes are compatible with current index mappings. @@ -196,11 +220,13 @@ export interface DoneState extends PostInitState { * need to make sure that no older Kibana versions are still writing to target * index. */ -export interface PrepareCompatibleMigration extends PostInitState { +export interface PrepareCompatibleMigration extends PostInitWithSource { /** We have found a schema-compatible migration, this means we can optimise our migration steps */ readonly controlState: 'PREPARE_COMPATIBLE_MIGRATION'; /** Alias-level actions that prepare for this migration */ readonly preTransformDocsActions: AliasAction[]; + /** Indicates whether we must refresh the index */ + readonly mustRefresh?: boolean; } export interface FatalState extends BaseState { @@ -210,30 +236,26 @@ export interface FatalState extends BaseState { readonly reason: string; } -export interface WaitForYellowSourceState extends BaseState { +export interface WaitForYellowSourceState extends BaseWithSource { /** Wait for the source index to be yellow before reading from it. */ readonly controlState: 'WAIT_FOR_YELLOW_SOURCE'; - readonly sourceIndex: Option.Some; readonly sourceIndexMappings: IndexMapping; readonly aliases: Record; } -export interface CheckUnknownDocumentsState extends BaseState { +export interface CheckUnknownDocumentsState extends BaseWithSource { /** Check if any unknown document is present in the source index */ readonly controlState: 'CHECK_UNKNOWN_DOCUMENTS'; - readonly sourceIndex: Option.Some; readonly sourceIndexMappings: IndexMapping; } -export interface SetSourceWriteBlockState extends PostInitState { +export interface SetSourceWriteBlockState extends PostInitWithSource { /** Set a write block on the source index to prevent any further writes */ readonly controlState: 'SET_SOURCE_WRITE_BLOCK'; - readonly sourceIndex: Option.Some; } -export interface CalculateExcludeFiltersState extends PostInitState { +export interface CalculateExcludeFiltersState extends PostInitWithSource { readonly controlState: 'CALCULATE_EXCLUDE_FILTERS'; - readonly sourceIndex: Option.Some; } export interface CreateNewTargetState extends PostInitState { @@ -243,19 +265,17 @@ export interface CreateNewTargetState extends PostInitState { readonly versionIndexReadyActions: Option.Some; } -export interface CreateReindexTempState extends PostInitState { +export interface CreateReindexTempState extends PostInitWithSource { /** * Create a target index with mappings from the source index and registered * plugins */ readonly controlState: 'CREATE_REINDEX_TEMP'; - readonly sourceIndex: Option.Some; } -export interface ReindexSourceToTempOpenPit extends PostInitState { +export interface ReindexSourceToTempOpenPit extends PostInitWithSource { /** Open PIT to the source index */ readonly controlState: 'REINDEX_SOURCE_TO_TEMP_OPEN_PIT'; - readonly sourceIndex: Option.Some; } interface ReindexSourceToTempBatch extends PostInitState { @@ -282,24 +302,19 @@ export interface ReindexSourceToTempTransform extends ReindexSourceToTempBatch { export interface ReindexSourceToTempIndexBulk extends ReindexSourceToTempBatch { readonly controlState: 'REINDEX_SOURCE_TO_TEMP_INDEX_BULK'; - readonly transformedDocBatches: [SavedObjectsRawDoc[]]; + readonly bulkOperationBatches: BulkOperation[][]; readonly currentBatch: number; } -export type SetTempWriteBlock = PostInitState & { - /** - * - */ +export interface SetTempWriteBlock extends PostInitWithSource { readonly controlState: 'SET_TEMP_WRITE_BLOCK'; - readonly sourceIndex: Option.Some; -}; +} -export interface CloneTempToSource extends PostInitState { +export interface CloneTempToSource extends PostInitWithSource { /** * Clone the temporary reindex index into */ readonly controlState: 'CLONE_TEMP_TO_TARGET'; - readonly sourceIndex: Option.Some; } export interface RefreshTarget extends PostInitState { @@ -380,7 +395,7 @@ export interface TransformedDocumentsBulkIndex extends PostInitState { * Write the up-to-date transformed documents to the target index */ readonly controlState: 'TRANSFORMED_DOCUMENTS_BULK_INDEX'; - readonly transformedDocBatches: SavedObjectsRawDoc[][]; + readonly bulkOperationBatches: BulkOperation[][]; readonly currentBatch: number; readonly lastHitSortValue: number[] | undefined; readonly hasTransformedDocs: boolean; @@ -423,8 +438,7 @@ export interface MarkVersionIndexReadyConflict extends PostInitState { * If we're migrating from a legacy index we need to perform some additional * steps to prepare this index so that it can be used as a migration 'source'. */ -export interface LegacyBaseState extends PostInitState { - readonly sourceIndex: Option.Some; +export interface LegacyBaseState extends PostInitWithSource { readonly legacyPreMigrationDoneActions: AliasAction[]; /** * The mappings read from the legacy index, used to create a new reindex @@ -474,6 +488,8 @@ export type State = Readonly< | FatalState | InitState | PrepareCompatibleMigration + | CleanupUnknownAndExcluded + | CleanupUnknownAndExcludedWaitForTaskState | WaitForMigrationCompletionState | DoneState | WaitForYellowSourceState diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/actions/index.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/actions/index.ts new file mode 100644 index 0000000000000..92334d396adc0 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/actions/index.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { + IncompatibleClusterRoutingAllocation, + RetryableEsClientError, + WaitForTaskCompletionTimeout, + IndexNotFound, +} from '../../actions'; + +export { + initAction as init, + type InitActionParams, + type IncompatibleClusterRoutingAllocation, + type RetryableEsClientError, + type WaitForTaskCompletionTimeout, + type IndexNotFound, +} from '../../actions'; + +export interface ActionErrorTypeMap { + wait_for_task_completion_timeout: WaitForTaskCompletionTimeout; + incompatible_cluster_routing_allocation: IncompatibleClusterRoutingAllocation; + retryable_es_client_error: RetryableEsClientError; + index_not_found_exception: IndexNotFound; +} + +/** Type guard for narrowing the type of a left */ +export function isTypeof( + res: any, + typeString: T +): res is ActionErrorTypeMap[T] { + return res.type === typeString; +} diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/context/create_context.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/context/create_context.ts new file mode 100644 index 0000000000000..cc4c7b63d3993 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/context/create_context.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { MigratorContext } from './types'; +import type { MigrateIndexOptions } from '../migrate_index'; + +export type CreateContextOps = Omit; + +/** + * Create the context object that will be used for this index migration. + */ +export const createContext = ({ + types, + docLinks, + migrationConfig, + elasticsearchClient, + indexPrefix, + typeRegistry, + serializer, +}: CreateContextOps): MigratorContext => { + return { + indexPrefix, + types, + elasticsearchClient, + typeRegistry, + serializer, + maxRetryAttempts: migrationConfig.retryAttempts, + migrationDocLinks: docLinks.links.kibanaUpgradeSavedObjects, + }; +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/context/index.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/context/index.ts new file mode 100644 index 0000000000000..9e7adc4c93868 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/context/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export type { MigratorContext } from './types'; +export { createContext, type CreateContextOps } from './create_context'; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/context/types.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/context/types.ts new file mode 100644 index 0000000000000..2603a5b69a681 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/context/types.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; +import type { + ISavedObjectTypeRegistry, + ISavedObjectsSerializer, +} from '@kbn/core-saved-objects-server'; +import type { DocLinks } from '@kbn/doc-links'; + +/** + * The set of static, precomputed values and services used by the ZDT migration + */ +export interface MigratorContext { + /** The first part of the index name such as `.kibana` or `.kibana_task_manager` */ + readonly indexPrefix: string; + /** Name of the types that are living in the index */ + readonly types: string[]; + /** The client to use for communications with ES */ + readonly elasticsearchClient: ElasticsearchClient; + /** The maximum number of retries to attempt for a failing action */ + readonly maxRetryAttempts: number; + /** DocLinks for savedObjects. to reference online documentation */ + readonly migrationDocLinks: DocLinks['kibanaUpgradeSavedObjects']; + /** SO serializer to use for migration */ + readonly serializer: ISavedObjectsSerializer; + /** The SO type registry to use for the migration */ + readonly typeRegistry: ISavedObjectTypeRegistry; +} diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/index.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/index.ts new file mode 100644 index 0000000000000..99e5c2d9cec44 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { runZeroDowntimeMigration, type RunZeroDowntimeMigrationOpts } from './run_zdt_migration'; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/migrate_index.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/migrate_index.ts new file mode 100644 index 0000000000000..72ee369236e16 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/migrate_index.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 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 { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; +import { + type SavedObjectsMigrationConfigType, + type MigrationResult, +} from '@kbn/core-saved-objects-base-server-internal'; +import type { + ISavedObjectTypeRegistry, + ISavedObjectsSerializer, +} from '@kbn/core-saved-objects-server'; +import type { Logger } from '@kbn/logging'; +import type { DocLinksServiceStart } from '@kbn/core-doc-links-server'; +import { migrationStateActionMachine } from './migration_state_action_machine'; +import type { VersionedTransformer } from '../document_migrator'; +import { createContext } from './context'; +import { next } from './next'; +import { model } from './model'; +import { createInitialState } from './state'; + +export interface MigrateIndexOptions { + indexPrefix: string; + types: string[]; + /** The SO type registry to use for the migration */ + typeRegistry: ISavedObjectTypeRegistry; + /** Logger to use for migration output */ + logger: Logger; + /** The document migrator to use to convert the document */ + documentMigrator: VersionedTransformer; + /** The migration config to use for the migration */ + migrationConfig: SavedObjectsMigrationConfigType; + /** docLinks contract to use to link to documentation */ + docLinks: DocLinksServiceStart; + /** SO serializer to use for migration */ + serializer: ISavedObjectsSerializer; + /** The client to use for communications with ES */ + elasticsearchClient: ElasticsearchClient; +} + +export const migrateIndex = async ({ + logger, + ...options +}: MigrateIndexOptions): Promise => { + const context = createContext(options); + const initialState = createInitialState(context); + + return migrationStateActionMachine({ + initialState, + next: next(context), + model, + context, + logger, + }); +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/migration_state_action_machine.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/migration_state_action_machine.ts new file mode 100644 index 0000000000000..8982a1a9c6c7e --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/migration_state_action_machine.ts @@ -0,0 +1,150 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { errors as EsErrors } from '@elastic/elasticsearch'; +import type { Logger } from '@kbn/logging'; +import { + getErrorMessage, + getRequestDebugMeta, +} from '@kbn/core-elasticsearch-client-server-internal'; +import { logStateTransition, logActionResponse } from '../common/utils'; +import { type Next, stateActionMachine } from '../state_action_machine'; +import { cleanup } from '../migrations_state_machine_cleanup'; +import type { State } from './state'; +import type { MigratorContext } from './context'; + +/** + * A specialized migrations-specific state-action machine that: + * - logs messages in state.logs + * - logs state transitions + * - logs action responses + * - resolves if the final state is DONE + * - rejects if the final state is FATAL + * - catches and logs exceptions and then rejects with a migrations specific error + */ +export async function migrationStateActionMachine({ + initialState, + context, + next, + model, + logger, +}: { + initialState: State; + context: MigratorContext; + next: Next; + model: (state: State, res: any, context: MigratorContext) => State; + logger: Logger; +}) { + const startTime = Date.now(); + // Since saved object index names usually start with a `.` and can be + // configured by users to include several `.`'s we can't use a logger tag to + // indicate which messages come from which index upgrade. + const logMessagePrefix = `[${context.indexPrefix}] `; + let prevTimestamp = startTime; + let lastState: State | undefined; + try { + const finalState = await stateActionMachine( + initialState, + (state) => next(state), + (state, res) => { + lastState = state; + logActionResponse(logger, logMessagePrefix, state, res); + const newState = model(state, res, context); + // Redact the state to reduce the memory consumption and so that we + // don't log sensitive information inside documents by only keeping + // the _id's of documents + const redactedNewState = { + ...newState, + /* TODO: commented until we have model stages that process outdated docs. (attrs not on model atm) + ...{ + outdatedDocuments: ( + (newState as ReindexSourceToTempTransform).outdatedDocuments ?? [] + ).map( + (doc) => + ({ + _id: doc._id, + } as SavedObjectsRawDoc) + ), + }, + ...{ + transformedDocBatches: ( + (newState as ReindexSourceToTempIndexBulk).transformedDocBatches ?? [] + ).map((batches) => batches.map((doc) => ({ _id: doc._id }))) as [SavedObjectsRawDoc[]], + }, + */ + }; + + const now = Date.now(); + logStateTransition( + logger, + logMessagePrefix, + state, + redactedNewState as State, + now - prevTimestamp + ); + prevTimestamp = now; + return newState; + } + ); + + const elapsedMs = Date.now() - startTime; + if (finalState.controlState === 'DONE') { + logger.info(logMessagePrefix + `Migration completed after ${Math.round(elapsedMs)}ms`); + return { + status: 'patched' as const, + destIndex: context.indexPrefix, + elapsedMs, + }; + } else if (finalState.controlState === 'FATAL') { + try { + await cleanup(context.elasticsearchClient, finalState); + } catch (e) { + logger.warn('Failed to cleanup after migrations:', e.message); + } + return Promise.reject( + new Error( + `Unable to complete saved object migrations for the [${context.indexPrefix}] index: ` + + finalState.reason + ) + ); + } else { + throw new Error('Invalid terminating control state'); + } + } catch (e) { + try { + await cleanup(context.elasticsearchClient, lastState); + } catch (err) { + logger.warn('Failed to cleanup after migrations:', err.message); + } + if (e instanceof EsErrors.ResponseError) { + // Log the failed request. This is very similar to the + // elasticsearch-service's debug logs, but we log everything in single + // line until we have sub-ms resolution in our cloud logs. Because this + // is error level logs, we're also more careful and don't log the request + // body since this can very likely have sensitive saved objects. + const req = getRequestDebugMeta(e.meta); + const failedRequestMessage = `Unexpected Elasticsearch ResponseError: statusCode: ${ + req.statusCode + }, method: ${req.method}, url: ${req.url} error: ${getErrorMessage(e)},`; + logger.error(logMessagePrefix + failedRequestMessage); + throw new Error( + `Unable to complete saved object migrations for the [${context.indexPrefix}] index. Please check the health of your Elasticsearch cluster and try again. ${failedRequestMessage}` + ); + } else { + logger.error(e); + + const newError = new Error( + `Unable to complete saved object migrations for the [${context.indexPrefix}] index. ${e}` + ); + + // restore error stack to point to a source of the problem. + newError.stack = `[${e.stack}]`; + throw newError; + } + } +} diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/index.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/index.ts new file mode 100644 index 0000000000000..22733dcbc531b --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { model } from './model'; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/model.test.mocks.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/model.test.mocks.ts new file mode 100644 index 0000000000000..70faab1fdc8d5 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/model.test.mocks.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 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. + */ + +const realStages = jest.requireActual('./stages'); + +export const StageMocks = Object.keys(realStages).reduce((mocks, key) => { + mocks[key] = jest.fn().mockImplementation((state: unknown) => state); + return mocks; +}, {} as Record); + +jest.doMock('./stages', () => { + return StageMocks; +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/model.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/model.test.ts new file mode 100644 index 0000000000000..b6c91e702bd2b --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/model.test.ts @@ -0,0 +1,134 @@ +/* + * Copyright 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 { StageMocks } from './model.test.mocks'; +import * as Either from 'fp-ts/lib/Either'; +import { createContextMock, MockedMigratorContext } from '../test_helpers'; +import type { RetryableEsClientError } from '../../actions'; +import type { State, BaseState, FatalState } from '../state'; +import type { StateActionResponse } from './types'; +import { model } from './model'; + +describe('model', () => { + let context: MockedMigratorContext; + + beforeEach(() => { + context = createContextMock(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + const baseState: BaseState = { + controlState: '42', + retryCount: 0, + retryDelay: 0, + logs: [], + }; + + const retryableError: RetryableEsClientError = { + type: 'retryable_es_client_error', + message: 'snapshot_in_progress_exception', + }; + + describe('retry behavior', () => { + test('increments retryCount, exponential retryDelay if an action fails with a retryable_es_client_error', () => { + let state: State = { + ...baseState, + controlState: 'INIT', + }; + + const states = new Array(5).fill(1).map(() => { + state = model(state, Either.left(retryableError), context); + return state; + }); + const retryState = states.map(({ retryCount, retryDelay }) => ({ retryCount, retryDelay })); + expect(retryState).toMatchInlineSnapshot(` + Array [ + Object { + "retryCount": 1, + "retryDelay": 2000, + }, + Object { + "retryCount": 2, + "retryDelay": 4000, + }, + Object { + "retryCount": 3, + "retryDelay": 8000, + }, + Object { + "retryCount": 4, + "retryDelay": 16000, + }, + Object { + "retryCount": 5, + "retryDelay": 32000, + }, + ] + `); + }); + + test('resets retryCount, retryDelay when an action succeeds', () => { + const state: State = { + ...baseState, + controlState: 'INIT', + retryCount: 5, + retryDelay: 32000, + }; + const res: StateActionResponse<'INIT'> = Either.right({ + '.kibana_7.11.0_001': { + aliases: {}, + mappings: { properties: {} }, + settings: {}, + }, + }); + const newState = model(state, res, context); + + expect(newState.retryCount).toEqual(0); + expect(newState.retryDelay).toEqual(0); + }); + + test('terminates to FATAL after retryAttempts retries', () => { + const state: State = { + ...baseState, + controlState: 'INIT', + retryCount: 15, + retryDelay: 64000, + }; + + const newState = model(state, Either.left(retryableError), context) as FatalState; + + expect(newState.controlState).toEqual('FATAL'); + expect(newState.reason).toMatchInlineSnapshot( + `"Unable to complete the INIT step after 15 attempts, terminating. The last failure message was: snapshot_in_progress_exception"` + ); + }); + }); + + describe('dispatching to correct stage', () => { + test('dispatching INIT state', () => { + const state: State = { + ...baseState, + controlState: 'INIT', + }; + const res: StateActionResponse<'INIT'> = Either.right({ + '.kibana_7.11.0_001': { + aliases: {}, + mappings: { properties: {} }, + settings: {}, + }, + }); + model(state, res, context); + + expect(StageMocks.init).toHaveBeenCalledTimes(1); + expect(StageMocks.init).toHaveBeenCalledWith(state, res, context); + }); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/model.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/model.ts new file mode 100644 index 0000000000000..2ea48bd1a58af --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/model.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 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 * as Either from 'fp-ts/lib/Either'; +import type { State, AllActionStates } from '../state'; +import type { ResponseType } from '../next'; +import { delayRetryState, resetRetryState } from '../../model/retry_state'; +import { throwBadControlState } from '../../model/helpers'; +import { isTypeof } from '../actions'; +import { MigratorContext } from '../context'; +import * as Stages from './stages'; +import { StateActionResponse } from './types'; + +export const model = ( + current: State, + response: ResponseType, + context: MigratorContext +): State => { + if (Either.isLeft(response)) { + if (isTypeof(response.left, 'retryable_es_client_error')) { + return delayRetryState(current, response.left.message, context.maxRetryAttempts); + } + } else { + current = resetRetryState(current); + } + + switch (current.controlState) { + case 'INIT': + return Stages.init(current, response as StateActionResponse<'INIT'>, context); + case 'DONE': + case 'FATAL': + // The state-action machine will never call the model in the terminating states + return throwBadControlState(current as never); + default: + return throwBadControlState(current); + } +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/index.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/index.ts new file mode 100644 index 0000000000000..aa12c6bed1b22 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { init } from './init'; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/init.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/init.test.ts new file mode 100644 index 0000000000000..8105449c7fce8 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/init.test.ts @@ -0,0 +1,61 @@ +/* + * Copyright 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 * as Either from 'fp-ts/lib/Either'; +import { createContextMock, MockedMigratorContext } from '../../test_helpers'; +import type { InitState } from '../../state'; +import type { StateActionResponse } from '../types'; +import { init } from './init'; + +describe('Action: init', () => { + let context: MockedMigratorContext; + + const createState = (parts: Partial = {}): InitState => ({ + controlState: 'INIT', + retryDelay: 0, + retryCount: 0, + logs: [], + ...parts, + }); + + beforeEach(() => { + context = createContextMock(); + }); + + test('INIT -> DONE because its not implemented yet', () => { + const state = createState(); + const res: StateActionResponse<'INIT'> = Either.right({ + '.kibana_8.7.0_001': { + aliases: { + '.kibana': {}, + '.kibana_8.7.0': {}, + }, + mappings: { properties: {} }, + settings: {}, + }, + }); + + const newState = init(state, res, context); + + expect(newState.controlState).toEqual('DONE'); + }); + + test('INIT -> INIT when cluster routing allocation is incompatible', () => { + const state = createState(); + const res: StateActionResponse<'INIT'> = Either.left({ + type: 'incompatible_cluster_routing_allocation', + }); + + const newState = init(state, res, context); + + expect(newState.controlState).toEqual('INIT'); + expect(newState.retryCount).toEqual(1); + expect(newState.retryDelay).toEqual(2000); + expect(newState.logs).toHaveLength(1); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/init.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/init.ts new file mode 100644 index 0000000000000..78dccf237afca --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/init.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as Either from 'fp-ts/lib/Either'; +import { delayRetryState } from '../../../model/retry_state'; +import { throwBadResponse } from '../../../model/helpers'; +import { isTypeof } from '../../actions'; +import type { State } from '../../state'; +import type { ModelStage } from '../types'; + +export const init: ModelStage<'INIT', 'DONE' | 'FATAL'> = (state, res, context): State => { + if (Either.isLeft(res)) { + const left = res.left; + if (isTypeof(left, 'incompatible_cluster_routing_allocation')) { + const retryErrorMessage = `[${left.type}] Incompatible Elasticsearch cluster settings detected. Remove the persistent and transient Elasticsearch cluster setting 'cluster.routing.allocation.enable' or set it to a value of 'all' to allow migrations to proceed. Refer to ${context.migrationDocLinks.routingAllocationDisabled} for more information on how to resolve the issue.`; + return delayRetryState(state, retryErrorMessage, context.maxRetryAttempts); + } else { + return throwBadResponse(state, left); + } + } + + // nothing implemented yet, just going to 'DONE' + return { + ...state, + controlState: 'DONE', + }; +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/types.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/types.ts new file mode 100644 index 0000000000000..6c8227794bb83 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/types.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 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 { ExcludeRetryableEsError } from '../../model/types'; +import type { MigratorContext } from '../context'; +import type { + AllActionStates, + AllControlStates, + StateFromActionState, + StateFromControlState, +} from '../state'; +import type { ResponseType } from '../next'; + +/** + * Utility type used to define the input of stage functions + */ +export type StateActionResponse = ExcludeRetryableEsError< + ResponseType +>; + +/** + * Defines a stage delegation function for the model + */ +export type ModelStage = ( + state: StateFromActionState, + res: StateActionResponse, + context: MigratorContext +) => StateFromControlState; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/next.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/next.ts new file mode 100644 index 0000000000000..329ba194b5957 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/next.ts @@ -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 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 { AllActionStates, InitState, State } from './state'; +import type { MigratorContext } from './context'; +import * as Actions from './actions'; + +export type ActionMap = ReturnType; + +/** + * The response type of the provided control state's action. + * + * E.g. given 'INIT', provides the response type of the action triggered by + * `next` in the 'INIT' control state. + */ +export type ResponseType = Awaited< + ReturnType> +>; + +export const nextActionMap = (context: MigratorContext) => { + return { + INIT: (state: InitState) => + Actions.init({ client: context.elasticsearchClient, indices: [context.indexPrefix] }), + }; +}; + +export const next = (context: MigratorContext) => { + const map = nextActionMap(context); + + return (state: State) => { + const delay = any>(fn: F): (() => ReturnType) => { + return () => { + return state.retryDelay > 0 + ? new Promise((resolve) => setTimeout(resolve, state.retryDelay)).then(fn) + : fn(); + }; + }; + + if (state.controlState === 'DONE' || state.controlState === 'FATAL') { + // Return null if we're in one of the terminating states + return null; + } else { + // Otherwise return the delayed action + // We use an explicit cast as otherwise TS infers `(state: never) => ...` + // here because state is inferred to be the intersection of all states + // instead of the union. + const nextAction = map[state.controlState] as ( + state: State + ) => ReturnType; + return delay(nextAction(state)); + } + }; +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/run_zdt_migration.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/run_zdt_migration.ts new file mode 100644 index 0000000000000..8a2686e23764b --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/run_zdt_migration.ts @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { Logger } from '@kbn/logging'; +import type { DocLinksServiceStart } from '@kbn/core-doc-links-server'; +import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; +import type { + ISavedObjectTypeRegistry, + ISavedObjectsSerializer, +} from '@kbn/core-saved-objects-server'; +import { + type SavedObjectsMigrationConfigType, + type MigrationResult, +} from '@kbn/core-saved-objects-base-server-internal'; +import type { VersionedTransformer } from '../document_migrator'; +import { buildMigratorConfigs } from './utils'; +import { migrateIndex } from './migrate_index'; + +export interface RunZeroDowntimeMigrationOpts { + /** The kibana system index prefix. e.g `.kibana` */ + kibanaIndexPrefix: string; + /** The SO type registry to use for the migration */ + typeRegistry: ISavedObjectTypeRegistry; + /** Logger to use for migration output */ + logger: Logger; + /** The document migrator to use to convert the document */ + documentMigrator: VersionedTransformer; + /** The migration config to use for the migration */ + migrationConfig: SavedObjectsMigrationConfigType; + /** docLinks contract to use to link to documentation */ + docLinks: DocLinksServiceStart; + /** SO serializer to use for migration */ + serializer: ISavedObjectsSerializer; + /** The client to use for communications with ES */ + elasticsearchClient: ElasticsearchClient; +} + +export const runZeroDowntimeMigration = async ( + options: RunZeroDowntimeMigrationOpts +): Promise => { + const migratorConfigs = buildMigratorConfigs({ + kibanaIndexPrefix: options.kibanaIndexPrefix, + typeRegistry: options.typeRegistry, + }); + + return await Promise.all( + migratorConfigs.map((migratorConfig) => { + return migrateIndex({ + ...options, + ...migratorConfig, + }); + }) + ); +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/state/create_initial_state.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/state/create_initial_state.ts new file mode 100644 index 0000000000000..ceb68b42b4a72 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/state/create_initial_state.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { InitState, State } from './types'; +import type { MigratorContext } from '../context'; + +export const createInitialState = (context: MigratorContext): State => { + const initialState: InitState = { + controlState: 'INIT', + logs: [], + retryCount: 0, + retryDelay: 0, + }; + return initialState; +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/state/index.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/state/index.ts new file mode 100644 index 0000000000000..bff3ea15da4c2 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/state/index.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export type { + BaseState, + InitState, + DoneState, + FatalState, + State, + AllActionStates, + AllControlStates, + StateFromActionState, + StateFromControlState, +} from './types'; +export { createInitialState } from './create_initial_state'; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/state/types.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/state/types.ts new file mode 100644 index 0000000000000..90a888a8a947c --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/state/types.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 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 { MigrationLog } from '../../types'; +import type { ControlState } from '../../state_action_machine'; + +export interface BaseState extends ControlState { + readonly retryCount: number; + readonly retryDelay: number; + readonly logs: MigrationLog[]; +} + +export interface InitState extends BaseState { + readonly controlState: 'INIT'; +} + +/** Migration completed successfully */ +export interface DoneState extends BaseState { + readonly controlState: 'DONE'; +} + +/** Migration terminated with a failure */ +export interface FatalState extends BaseState { + readonly controlState: 'FATAL'; + /** The reason the migration was terminated */ + readonly reason: string; +} + +export type State = InitState | DoneState | FatalState; + +export type AllControlStates = State['controlState']; + +export type AllActionStates = Exclude; + +/** + * Manually maintained reverse-lookup map used by `StateFromAction` + */ +export interface ControlStateMap { + INIT: InitState; + FATAL: FatalState; + DONE: DoneState; +} + +/** + * Utility type to reverse lookup an `AllControlStates` to it's corresponding State subtype. + */ +export type StateFromControlState = ControlStateMap[T]; + +/** + * Utility type to reverse lookup an `AllActionStates` to it's corresponding State subtype. + */ +export type StateFromActionState = StateFromControlState; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/test_helpers/context.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/test_helpers/context.ts new file mode 100644 index 0000000000000..ded69aa02e7de --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/test_helpers/context.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { + ElasticsearchClientMock, + elasticsearchClientMock, +} from '@kbn/core-elasticsearch-client-server-mocks'; +import { SavedObjectTypeRegistry } from '@kbn/core-saved-objects-base-server-internal'; +import { serializerMock } from '@kbn/core-saved-objects-base-server-mocks'; +import { docLinksServiceMock } from '@kbn/core-doc-links-server-mocks'; +import type { MigratorContext } from '../context'; + +export type MockedMigratorContext = Omit & { + elasticsearchClient: ElasticsearchClientMock; +}; + +export const createContextMock = ( + parts: Partial = {} +): MockedMigratorContext => { + const typeRegistry = new SavedObjectTypeRegistry(); + + return { + indexPrefix: '.kibana', + types: ['foo', 'bar'], + elasticsearchClient: elasticsearchClientMock.createElasticsearchClient(), + maxRetryAttempts: 15, + migrationDocLinks: docLinksServiceMock.createSetupContract().links.kibanaUpgradeSavedObjects, + typeRegistry, + serializer: serializerMock.create(), + ...parts, + }; +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/test_helpers/index.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/test_helpers/index.ts new file mode 100644 index 0000000000000..8b0c329317c03 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/test_helpers/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { createContextMock, type MockedMigratorContext } from './context'; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/get_migrator_configs.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/get_migrator_configs.ts new file mode 100644 index 0000000000000..79634c456c5d7 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/get_migrator_configs.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { ISavedObjectTypeRegistry } from '@kbn/core-saved-objects-server'; + +export interface MigratorConfig { + /** The index prefix for this migrator. e.g '.kibana' */ + indexPrefix: string; + /** The id of the types this migrator is in charge of */ + types: string[]; +} + +export const buildMigratorConfigs = ({ + typeRegistry, + kibanaIndexPrefix, +}: { + typeRegistry: ISavedObjectTypeRegistry; + kibanaIndexPrefix: string; +}): MigratorConfig[] => { + const configMap = new Map(); + typeRegistry.getAllTypes().forEach((type) => { + const typeIndexPrefix = type.indexPattern ?? kibanaIndexPrefix; + if (!configMap.has(typeIndexPrefix)) { + configMap.set(typeIndexPrefix, { + indexPrefix: typeIndexPrefix, + types: [], + }); + } + const migratorConfig = configMap.get(typeIndexPrefix)!; + migratorConfig.types.push(type.name); + }); + return [...configMap.values()]; +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/index.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/index.ts new file mode 100644 index 0000000000000..f96ea531e460d --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { buildMigratorConfigs, type MigratorConfig } from './get_migrator_configs'; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/tsconfig.json b/packages/core/saved-objects/core-saved-objects-migration-server-internal/tsconfig.json index d1751da1050f5..e56ee4e2c3234 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/tsconfig.json +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/tsconfig.json @@ -30,6 +30,7 @@ "@kbn/doc-links", "@kbn/safer-lodash-set", "@kbn/logging-mocks", + "@kbn/core-saved-objects-base-server-mocks", ], "exclude": [ "target/**/*", diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-mocks/src/kibana_migrator.mock.ts b/packages/core/saved-objects/core-saved-objects-migration-server-mocks/src/kibana_migrator.mock.ts index e7df2a0363a26..a0d7ac83fe74a 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-mocks/src/kibana_migrator.mock.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-mocks/src/kibana_migrator.mock.ts @@ -12,7 +12,10 @@ import type { IKibanaMigrator, KibanaMigratorStatus, } from '@kbn/core-saved-objects-base-server-internal'; -import { buildActiveMappings, mergeTypes } from '@kbn/core-saved-objects-migration-server-internal'; +import { + buildActiveMappings, + buildTypesMappings, +} from '@kbn/core-saved-objects-migration-server-internal'; const defaultSavedObjectTypes: SavedObjectsType[] = [ { @@ -57,7 +60,7 @@ const createMigrator = ( ), }; - mockMigrator.getActiveMappings.mockReturnValue(buildActiveMappings(mergeTypes(types))); + mockMigrator.getActiveMappings.mockReturnValue(buildActiveMappings(buildTypesMappings(types))); mockMigrator.migrateDocument.mockImplementation((doc) => doc); return mockMigrator; }; diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/saved_objects_service.test.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/saved_objects_service.test.ts index 6afe9eb18b4f9..fc2cc5a4c91bc 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/saved_objects_service.test.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/saved_objects_service.test.ts @@ -419,6 +419,7 @@ describe('SavedObjectsService', () => { startDeps.node = nodeServiceMock.createInternalStartContract({ ui: true, backgroundTasks: true, + migrator: false, }); await soService.start(startDeps); @@ -444,6 +445,7 @@ describe('SavedObjectsService', () => { startDeps.node = nodeServiceMock.createInternalStartContract({ ui: true, backgroundTasks: false, + migrator: false, }); await soService.start(startDeps); @@ -469,6 +471,7 @@ describe('SavedObjectsService', () => { startDeps.node = nodeServiceMock.createInternalStartContract({ ui: false, backgroundTasks: true, + migrator: false, }); await soService.start(startDeps); diff --git a/packages/kbn-alerts-as-data-utils/index.ts b/packages/kbn-alerts-as-data-utils/index.ts new file mode 100644 index 0000000000000..7b2cad2ca5440 --- /dev/null +++ b/packages/kbn-alerts-as-data-utils/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './src/field_maps'; diff --git a/packages/kbn-alerts-as-data-utils/kibana.jsonc b/packages/kbn-alerts-as-data-utils/kibana.jsonc new file mode 100644 index 0000000000000..07e8490dde7b5 --- /dev/null +++ b/packages/kbn-alerts-as-data-utils/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/alerts-as-data-utils", + "owner": "@elastic/response-ops" +} diff --git a/packages/kbn-alerts-as-data-utils/package.json b/packages/kbn-alerts-as-data-utils/package.json new file mode 100644 index 0000000000000..25aa26b3d435c --- /dev/null +++ b/packages/kbn-alerts-as-data-utils/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/alerts-as-data-utils", + "private": true, + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0" +} \ No newline at end of file diff --git a/x-pack/plugins/alerting/common/alert_schema/field_maps/alert_field_map.ts b/packages/kbn-alerts-as-data-utils/src/field_maps/alert_field_map.ts similarity index 77% rename from x-pack/plugins/alerting/common/alert_schema/field_maps/alert_field_map.ts rename to packages/kbn-alerts-as-data-utils/src/field_maps/alert_field_map.ts index 4613415e0fa00..8e5a606a91017 100644 --- a/x-pack/plugins/alerting/common/alert_schema/field_maps/alert_field_map.ts +++ b/packages/kbn-alerts-as-data-utils/src/field_maps/alert_field_map.ts @@ -1,16 +1,20 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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 { ALERT_ACTION_GROUP, + ALERT_CASE_IDS, ALERT_DURATION, ALERT_END, ALERT_FLAPPING, - ALERT_ID, + ALERT_FLAPPING_HISTORY, + ALERT_INSTANCE_ID, + ALERT_LAST_DETECTED, ALERT_REASON, ALERT_RULE_CATEGORY, ALERT_RULE_CONSUMER, @@ -27,92 +31,93 @@ import { ALERT_UUID, ALERT_WORKFLOW_STATUS, SPACE_IDS, + TIMESTAMP, VERSION, } from '@kbn/rule-data-utils'; export const alertFieldMap = { - [ALERT_RULE_PARAMETERS]: { - type: 'object', - enabled: false, + [ALERT_ACTION_GROUP]: { + type: 'keyword', + array: false, required: false, }, - [ALERT_RULE_TYPE_ID]: { + [ALERT_CASE_IDS]: { type: 'keyword', + array: true, + required: false, + }, + [ALERT_DURATION]: { + type: 'long', array: false, - required: true, + required: false, }, - [ALERT_RULE_CONSUMER]: { - type: 'keyword', + [ALERT_END]: { + type: 'date', array: false, - required: true, + required: false, }, - [ALERT_RULE_PRODUCER]: { - type: 'keyword', + [ALERT_FLAPPING]: { + type: 'boolean', array: false, - required: true, + required: false, }, - [SPACE_IDS]: { - type: 'keyword', + [ALERT_FLAPPING_HISTORY]: { + type: 'boolean', array: true, - required: true, - }, - [ALERT_UUID]: { - type: 'keyword', - array: false, - required: true, + required: false, }, - [ALERT_ID]: { + [ALERT_INSTANCE_ID]: { type: 'keyword', array: false, required: true, }, - [ALERT_START]: { + [ALERT_LAST_DETECTED]: { type: 'date', - array: false, required: false, - }, - [ALERT_TIME_RANGE]: { - type: 'date_range', - format: 'epoch_millis||strict_date_optional_time', array: false, - required: false, }, - [ALERT_END]: { - type: 'date', + [ALERT_REASON]: { + type: 'keyword', array: false, required: false, }, - [ALERT_DURATION]: { - type: 'long', + [ALERT_RULE_CATEGORY]: { + type: 'keyword', array: false, - required: false, + required: true, }, - [ALERT_STATUS]: { + [ALERT_RULE_CONSUMER]: { type: 'keyword', array: false, required: true, }, - [VERSION]: { - type: 'version', + [ALERT_RULE_EXECUTION_UUID]: { + type: 'keyword', array: false, required: false, }, - [ALERT_WORKFLOW_STATUS]: { + [ALERT_RULE_NAME]: { type: 'keyword', array: false, - required: false, + required: true, }, - [ALERT_ACTION_GROUP]: { - type: 'keyword', + [ALERT_RULE_PARAMETERS]: { array: false, + type: 'flattened', + ignore_above: 4096, required: false, }, - [ALERT_REASON]: { + [ALERT_RULE_PRODUCER]: { type: 'keyword', array: false, + required: true, + }, + [ALERT_RULE_TAGS]: { + type: 'keyword', + array: true, required: false, }, - [ALERT_RULE_CATEGORY]: { + [ALERT_RULE_TYPE_ID]: { type: 'keyword', array: false, required: true, @@ -122,26 +127,47 @@ export const alertFieldMap = { array: false, required: true, }, - [ALERT_RULE_EXECUTION_UUID]: { + [ALERT_START]: { + type: 'date', + array: false, + required: false, + }, + [ALERT_STATUS]: { type: 'keyword', array: false, + required: true, + }, + [ALERT_TIME_RANGE]: { + type: 'date_range', + format: 'epoch_millis||strict_date_optional_time', + array: false, required: false, }, - [ALERT_RULE_NAME]: { + [ALERT_UUID]: { type: 'keyword', array: false, required: true, }, - [ALERT_RULE_TAGS]: { + [ALERT_WORKFLOW_STATUS]: { type: 'keyword', - array: true, + array: false, required: false, }, - [ALERT_FLAPPING]: { - type: 'boolean', + [SPACE_IDS]: { + type: 'keyword', + array: true, + required: true, + }, + [TIMESTAMP]: { + type: 'date', + required: true, + array: false, + }, + [VERSION]: { + type: 'version', array: false, required: false, }, -}; +} as const; export type AlertFieldMap = typeof alertFieldMap; diff --git a/packages/kbn-alerts-as-data-utils/src/field_maps/ecs_field_map.ts b/packages/kbn-alerts-as-data-utils/src/field_maps/ecs_field_map.ts new file mode 100644 index 0000000000000..9294a12b4ce50 --- /dev/null +++ b/packages/kbn-alerts-as-data-utils/src/field_maps/ecs_field_map.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { EcsFlat } from '@kbn/ecs'; +import { EcsMetadata, FieldMap } from './types'; + +export const ecsFieldMap: FieldMap = Object.keys(EcsFlat).reduce((acc, currKey) => { + const value: EcsMetadata = EcsFlat[currKey as keyof typeof EcsFlat]; + return { + ...acc, + [currKey]: { + type: value.type, + array: value.normalize.includes('array'), + required: !!value.required, + ...(value.scaling_factor ? { scaling_factor: value.scaling_factor } : {}), + ...(value.ignore_above ? { ignore_above: value.ignore_above } : {}), + ...(value.multi_fields ? { multi_fields: value.multi_fields } : {}), + }, + }; +}, {}); + +export type EcsFieldMap = typeof ecsFieldMap; diff --git a/packages/kbn-alerts-as-data-utils/src/field_maps/index.ts b/packages/kbn-alerts-as-data-utils/src/field_maps/index.ts new file mode 100644 index 0000000000000..9aef7690b343c --- /dev/null +++ b/packages/kbn-alerts-as-data-utils/src/field_maps/index.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './alert_field_map'; +export * from './ecs_field_map'; +export * from './legacy_alert_field_map'; +export type { FieldMap, MultiField } from './types'; diff --git a/packages/kbn-alerts-as-data-utils/src/field_maps/legacy_alert_field_map.ts b/packages/kbn-alerts-as-data-utils/src/field_maps/legacy_alert_field_map.ts new file mode 100644 index 0000000000000..6faa403188fdb --- /dev/null +++ b/packages/kbn-alerts-as-data-utils/src/field_maps/legacy_alert_field_map.ts @@ -0,0 +1,202 @@ +/* + * Copyright 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 { + ALERT_RISK_SCORE, + ALERT_RULE_AUTHOR, + ALERT_RULE_CREATED_AT, + ALERT_RULE_CREATED_BY, + ALERT_RULE_DESCRIPTION, + ALERT_RULE_ENABLED, + ALERT_RULE_FROM, + ALERT_RULE_INTERVAL, + ALERT_RULE_LICENSE, + ALERT_RULE_NOTE, + ALERT_RULE_REFERENCES, + ALERT_RULE_RULE_ID, + ALERT_RULE_RULE_NAME_OVERRIDE, + ALERT_RULE_TO, + ALERT_RULE_TYPE, + ALERT_RULE_UPDATED_AT, + ALERT_RULE_UPDATED_BY, + ALERT_RULE_VERSION, + ALERT_SEVERITY, + ALERT_SUPPRESSION_DOCS_COUNT, + ALERT_SUPPRESSION_END, + ALERT_SUPPRESSION_FIELD, + ALERT_SUPPRESSION_START, + ALERT_SUPPRESSION_VALUE, + ALERT_SYSTEM_STATUS, + ALERT_WORKFLOW_REASON, + ALERT_WORKFLOW_USER, + ECS_VERSION, + EVENT_ACTION, + EVENT_KIND, + TAGS, +} from '@kbn/rule-data-utils'; + +export const legacyAlertFieldMap = { + [ALERT_RISK_SCORE]: { + type: 'float', + array: false, + required: false, + }, + [ALERT_RULE_AUTHOR]: { + type: 'keyword', + array: false, + required: false, + }, + [ALERT_RULE_CREATED_AT]: { + type: 'date', + array: false, + required: false, + }, + [ALERT_RULE_CREATED_BY]: { + type: 'keyword', + array: false, + required: false, + }, + [ALERT_RULE_DESCRIPTION]: { + type: 'keyword', + array: false, + required: false, + }, + [ALERT_RULE_ENABLED]: { + type: 'keyword', + array: false, + required: false, + }, + [ALERT_RULE_FROM]: { + type: 'keyword', + array: false, + required: false, + }, + [ALERT_RULE_INTERVAL]: { + type: 'keyword', + array: false, + required: false, + }, + [ALERT_RULE_LICENSE]: { + type: 'keyword', + array: false, + required: false, + }, + [ALERT_RULE_NOTE]: { + type: 'keyword', + array: false, + required: false, + }, + [ALERT_RULE_REFERENCES]: { + type: 'keyword', + array: true, + required: false, + }, + [ALERT_RULE_RULE_ID]: { + type: 'keyword', + array: false, + required: false, + }, + [ALERT_RULE_RULE_NAME_OVERRIDE]: { + type: 'keyword', + array: false, + required: false, + }, + [ALERT_RULE_TO]: { + type: 'keyword', + array: false, + required: false, + }, + [ALERT_RULE_TYPE]: { + type: 'keyword', + array: false, + required: false, + }, + [ALERT_RULE_UPDATED_AT]: { + type: 'date', + array: false, + required: false, + }, + [ALERT_RULE_UPDATED_BY]: { + type: 'keyword', + array: false, + required: false, + }, + [ALERT_RULE_VERSION]: { + type: 'keyword', + array: false, + required: false, + }, + [ALERT_SEVERITY]: { + type: 'keyword', + array: false, + required: false, + }, + [ALERT_SUPPRESSION_DOCS_COUNT]: { + type: 'long', + array: false, + required: false, + }, + [ALERT_SUPPRESSION_END]: { + type: 'date', + array: false, + required: false, + }, + [ALERT_SUPPRESSION_FIELD]: { + type: 'keyword', + array: true, + required: false, + }, + [ALERT_SUPPRESSION_START]: { + type: 'date', + array: false, + required: false, + }, + [ALERT_SUPPRESSION_VALUE]: { + type: 'keyword', + array: true, + required: false, + }, + [ALERT_SYSTEM_STATUS]: { + type: 'keyword', + array: false, + required: false, + }, + [ALERT_WORKFLOW_REASON]: { + type: 'keyword', + array: false, + required: false, + }, + [ALERT_WORKFLOW_USER]: { + type: 'keyword', + array: false, + required: false, + }, + // get these from ecs field map when available + [ECS_VERSION]: { + type: 'keyword', + array: false, + required: false, + }, + [EVENT_ACTION]: { + type: 'keyword', + array: false, + required: false, + }, + [EVENT_KIND]: { + type: 'keyword', + array: false, + required: false, + }, + [TAGS]: { + type: 'keyword', + array: true, + required: false, + }, +} as const; + +export type LegacyAlertFieldMap = typeof legacyAlertFieldMap; diff --git a/packages/kbn-alerts-as-data-utils/src/field_maps/types.ts b/packages/kbn-alerts-as-data-utils/src/field_maps/types.ts new file mode 100644 index 0000000000000..04f9d045f6e28 --- /dev/null +++ b/packages/kbn-alerts-as-data-utils/src/field_maps/types.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export interface AllowedValue { + description?: string; + name?: string; +} + +export interface MultiField { + flat_name: string; + name: string; + type: string; +} + +export interface EcsMetadata { + allowed_values?: AllowedValue[]; + dashed_name: string; + description: string; + doc_values?: boolean; + example?: string | number | boolean; + flat_name: string; + ignore_above?: number; + index?: boolean; + level: string; + multi_fields?: MultiField[]; + name: string; + normalize: string[]; + required?: boolean; + scaling_factor?: number; + short: string; + type: string; +} + +export interface FieldMap { + [key: string]: { + type: string; + required: boolean; + array?: boolean; + doc_values?: boolean; + enabled?: boolean; + format?: string; + ignore_above?: number; + multi_fields?: MultiField[]; + index?: boolean; + path?: string; + scaling_factor?: number; + dynamic?: boolean | 'strict'; + }; +} diff --git a/packages/kbn-alerts-as-data-utils/tsconfig.json b/packages/kbn-alerts-as-data-utils/tsconfig.json new file mode 100644 index 0000000000000..00b7ffc082c95 --- /dev/null +++ b/packages/kbn-alerts-as-data-utils/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts" + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/ecs", + "@kbn/rule-data-utils", + ] +} diff --git a/packages/kbn-cell-actions/src/hooks/use_load_actions.test.ts b/packages/kbn-cell-actions/src/hooks/use_load_actions.test.ts index 8df9102719466..11ec2f0984199 100644 --- a/packages/kbn-cell-actions/src/hooks/use_load_actions.test.ts +++ b/packages/kbn-cell-actions/src/hooks/use_load_actions.test.ts @@ -176,5 +176,28 @@ describe('loadActions hooks', () => { expect(result.current.value).toEqual([[actionEnabled], [actionEnabled]]); }); + + it('should re-render when contexts is changed', async () => { + const { result, rerender, waitForNextUpdate } = renderHook(useBulkLoadActions, { + initialProps: [actionContext], + }); + + await waitForNextUpdate(); + expect(mockGetActions).toHaveBeenCalledWith(actionContext); + + rerender([actionContext2]); + await waitForNextUpdate(); + expect(mockGetActions).toHaveBeenCalledWith(actionContext2); + + mockGetActions.mockClear(); + + rerender([]); + await waitForNextUpdate(); + expect(mockGetActions).toHaveBeenCalledTimes(0); + + expect(result.current.value).toBeInstanceOf(Array); + expect(result.current.value).toHaveLength(0); + expect(result.current.loading).toBe(false); + }); }); }); diff --git a/packages/kbn-cell-actions/src/hooks/use_load_actions.ts b/packages/kbn-cell-actions/src/hooks/use_load_actions.ts index 54e6843bac279..6eece48655be3 100644 --- a/packages/kbn-cell-actions/src/hooks/use_load_actions.ts +++ b/packages/kbn-cell-actions/src/hooks/use_load_actions.ts @@ -66,7 +66,7 @@ export const useBulkLoadActions = ( ) ) ), - [] + [contexts] ); useThrowError(error); return actionsState; diff --git a/packages/kbn-cypress-config/index.ts b/packages/kbn-cypress-config/index.ts index eed0d71288115..58abd54a56f69 100644 --- a/packages/kbn-cypress-config/index.ts +++ b/packages/kbn-cypress-config/index.ts @@ -51,6 +51,8 @@ export function defineCypressConfig(options?: Cypress.ConfigOptions) { on(event, task); }, config); + + return config; } }, }, diff --git a/packages/kbn-expandable-flyout/README.md b/packages/kbn-expandable-flyout/README.md new file mode 100644 index 0000000000000..8d21caba73a21 --- /dev/null +++ b/packages/kbn-expandable-flyout/README.md @@ -0,0 +1,66 @@ +# @kbn/expandable-flyout + +## Purpose + +This package offers an expandable flyout UI component and a way to manage the data displayed in it. The component leverages the [EuiFlyout](https://github.com/elastic/eui/tree/main/src/components/flyout) from the EUI library. + +The flyout is composed of 3 sections: +- a right section (primary section) that opens first +- a left wider section to show more details +- a preview section, that overlays the right section. This preview section can display multiple panels one after the other and displays a `Back` button + +At the moment, displaying more than one flyout within the same plugin might be complicated, unless there are in difference areas in the codebase and the contexts don't conflict with each other. + +## What the package offers + +The ExpandableFlyout [React component](https://github.com/elastic/kibana/tree/main/packages/kbn-expandable-flyout/src/components/index) that renders the UI. + +The ExpandableFlyout [React context](https://github.com/elastic/kibana/tree/main/packages/kbn-expandable-flyout/src/components/context) that exposes the following api: +- **openFlyout**: open the flyout with a set of panels +- **openFlyoutRightPanel**: open a right panel +- **openFlyoutLeftPanel**: open a left panel +- **openFlyoutPreviewPanel**: open a preview panel +- **closeFlyoutRightPanel**: close the right panel +- **closeFlyoutLeftPanel**: close the left panel +- **closeFlyoutPreviewPanel**: close the preview panels +- **previousFlyoutPreviewPanel**: navigate to the previous preview panel +- **closeFlyout**: close the flyout + +To retrieve the flyout's layout (left, right and preview panels), you can use the **panels** from the same [React context](https://github.com/elastic/kibana/tree/main/packages/kbn-expandable-flyout/src/components/context); + +- To have more details about how these above api work, see the code documentation [here](https://github.com/elastic/kibana/tree/main/packages/kbn-expandable-flyout/src/utils/helpers). + +## Usage + +To use the expandable flyout in your plugin, first you need wrap your code with the context provider at a high enough level as follows: +```typescript jsx + + + ... + + +``` + +Then use the React UI component where you need: + +```typescript jsx + +``` +where `myPanels` is a list of all the panels that can be rendered in the flyout (see interface [here](https://github.com/elastic/kibana/tree/main/packages/kbn-expandable-flyout/src/components/index)). + + +## Terminology + +### Section + +One of the 3 areas of the flyout (left, right or preview). + +### Panel + +A set of properties defining what's displayed in one of the flyout section. + +## Future work + +- currently the panels are aware of their width. This should be changed and the width of the left, right and preview sections should be handled by the flyout itself +- add the feature to save the flyout state (layout) to the url +- introduce the notion of scope to be able to handle more than one flyout per plugin?? \ No newline at end of file diff --git a/src/plugins/presentation_util/public/components/solution_toolbar/items/primary_button.scss b/packages/kbn-expandable-flyout/index.ts similarity index 60% rename from src/plugins/presentation_util/public/components/solution_toolbar/items/primary_button.scss rename to packages/kbn-expandable-flyout/index.ts index c3d89f430d70c..e2ce15d85a399 100644 --- a/src/plugins/presentation_util/public/components/solution_toolbar/items/primary_button.scss +++ b/packages/kbn-expandable-flyout/index.ts @@ -6,15 +6,8 @@ * Side Public License, v 1. */ -// Temporary fix for lensApp icon not support ghost color -.solutionToolbar__primaryButton--dark { - .euiIcon path { - fill: $euiColorInk; - } -} +export { ExpandableFlyout } from './src'; +export { ExpandableFlyoutProvider, useExpandableFlyoutContext } from './src/context'; -.solutionToolbar__primaryButton--light { - .euiIcon path { - fill: $euiColorGhost; - } -} +export type { ExpandableFlyoutProps } from './src'; +export type { FlyoutPanel } from './src/types'; diff --git a/packages/kbn-expandable-flyout/jest.config.js b/packages/kbn-expandable-flyout/jest.config.js new file mode 100644 index 0000000000000..f861f9b122fd5 --- /dev/null +++ b/packages/kbn-expandable-flyout/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-expandable-flyout'], +}; diff --git a/packages/kbn-expandable-flyout/kibana.jsonc b/packages/kbn-expandable-flyout/kibana.jsonc new file mode 100644 index 0000000000000..b4f63cca6bf91 --- /dev/null +++ b/packages/kbn-expandable-flyout/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/expandable-flyout", + "owner": "@elastic/security-threat-hunting-investigations" +} diff --git a/packages/kbn-expandable-flyout/package.json b/packages/kbn-expandable-flyout/package.json new file mode 100644 index 0000000000000..a2e826c042842 --- /dev/null +++ b/packages/kbn-expandable-flyout/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/expandable-flyout", + "private": true, + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0" +} \ No newline at end of file diff --git a/packages/kbn-expandable-flyout/src/actions.ts b/packages/kbn-expandable-flyout/src/actions.ts new file mode 100644 index 0000000000000..5709214394303 --- /dev/null +++ b/packages/kbn-expandable-flyout/src/actions.ts @@ -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 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 { FlyoutPanel } from './types'; + +export enum ActionType { + openFlyout = 'open_flyout', + openRightPanel = 'open_right_panel', + openLeftPanel = 'open_left_panel', + openPreviewPanel = 'open_preview_panel', + closeRightPanel = 'close_right_panel', + closeLeftPanel = 'close_left_panel', + closePreviewPanel = 'close_preview_panel', + previousPreviewPanel = 'previous_preview_panel', + closeFlyout = 'close_flyout', +} + +export type Action = + | { + type: ActionType.openFlyout; + payload: { + right?: FlyoutPanel; + left?: FlyoutPanel; + preview?: FlyoutPanel; + }; + } + | { + type: ActionType.openRightPanel; + payload: FlyoutPanel; + } + | { + type: ActionType.openLeftPanel; + payload: FlyoutPanel; + } + | { + type: ActionType.openPreviewPanel; + payload: FlyoutPanel; + } + | { + type: ActionType.closeRightPanel; + } + | { + type: ActionType.closeLeftPanel; + } + | { + type: ActionType.closePreviewPanel; + } + | { + type: ActionType.previousPreviewPanel; + } + | { + type: ActionType.closeFlyout; + }; diff --git a/packages/kbn-expandable-flyout/src/components/left_section.tsx b/packages/kbn-expandable-flyout/src/components/left_section.tsx new file mode 100644 index 0000000000000..ddf53efbad2b8 --- /dev/null +++ b/packages/kbn-expandable-flyout/src/components/left_section.tsx @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import React from 'react'; +import { LEFT_SECTION } from './test_ids'; + +interface LeftSectionProps { + /** + * Component to be rendered + */ + component: React.ReactElement; + /** + * Width used when rendering the panel + */ + width: number; +} + +/** + * Left section of the expanded flyout rendering a panel + */ +export const LeftSection: React.FC = ({ component, width }: LeftSectionProps) => { + return ( + + + {component} + + + ); +}; + +LeftSection.displayName = 'LeftSection'; diff --git a/packages/kbn-expandable-flyout/src/components/preview_section.test.tsx b/packages/kbn-expandable-flyout/src/components/preview_section.test.tsx new file mode 100644 index 0000000000000..41926400e11f5 --- /dev/null +++ b/packages/kbn-expandable-flyout/src/components/preview_section.test.tsx @@ -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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { PreviewSection } from './preview_section'; +import { PREVIEW_SECTION_BACK_BUTTON, PREVIEW_SECTION_CLOSE_BUTTON } from './test_ids'; +import { ExpandableFlyoutContext } from '../context'; + +describe('PreviewSection', () => { + const context: ExpandableFlyoutContext = { + panels: { + right: {}, + left: {}, + preview: [ + { + id: 'key', + }, + ], + }, + } as unknown as ExpandableFlyoutContext; + + it('should render close button in header', () => { + const component =
    {'component'}
    ; + const width = 500; + const showBackButton = false; + + const { getByTestId } = render( + + + + ); + + expect(getByTestId(PREVIEW_SECTION_CLOSE_BUTTON)).toBeInTheDocument(); + }); + + it('should render back button in header', () => { + const component =
    {'component'}
    ; + const width = 500; + const showBackButton = true; + + const { getByTestId } = render( + + + + ); + + expect(getByTestId(PREVIEW_SECTION_BACK_BUTTON)).toBeInTheDocument(); + }); +}); diff --git a/packages/kbn-expandable-flyout/src/components/preview_section.tsx b/packages/kbn-expandable-flyout/src/components/preview_section.tsx new file mode 100644 index 0000000000000..e474d1204bf03 --- /dev/null +++ b/packages/kbn-expandable-flyout/src/components/preview_section.tsx @@ -0,0 +1,124 @@ +/* + * Copyright 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 { + EuiButtonEmpty, + EuiButtonIcon, + EuiFlexGroup, + EuiFlexItem, + EuiPanel, + useEuiTheme, +} from '@elastic/eui'; +import React from 'react'; +import { css } from '@emotion/react'; +import { + PREVIEW_SECTION, + PREVIEW_SECTION_BACK_BUTTON, + PREVIEW_SECTION_CLOSE_BUTTON, +} from './test_ids'; +import { useExpandableFlyoutContext } from '../..'; +import { BACK_BUTTON, CLOSE_BUTTON } from './translations'; + +interface PreviewSectionProps { + /** + * Component to be rendered + */ + component: React.ReactElement; + /** + * Width used when rendering the panel + */ + width: number | undefined; + /** + * Display the back button in the header + */ + showBackButton: boolean; +} + +/** + * Preview section of the expanded flyout rendering one or multiple panels. + * Will display a back and close button in the header for the previous and close feature respectively. + */ +export const PreviewSection: React.FC = ({ + component, + showBackButton, + width, +}: PreviewSectionProps) => { + const { euiTheme } = useEuiTheme(); + const { closePreviewPanel, previousPreviewPanel } = useExpandableFlyoutContext(); + + const previewWith: string = width ? `${width}px` : '0px'; + + const closeButton = ( + + closePreviewPanel()} + data-test-subj={PREVIEW_SECTION_CLOSE_BUTTON} + aria-label={CLOSE_BUTTON} + /> + + ); + const header = showBackButton ? ( + + + previousPreviewPanel()} + data-test-subj={PREVIEW_SECTION_BACK_BUTTON} + aria-label={BACK_BUTTON} + > + {BACK_BUTTON} + + + {closeButton} + + ) : ( + {closeButton} + ); + + return ( + <> +
    +
    + + {header} + {component} + +
    + + ); +}; + +PreviewSection.displayName = 'PreviewSection'; diff --git a/packages/kbn-expandable-flyout/src/components/right_section.tsx b/packages/kbn-expandable-flyout/src/components/right_section.tsx new file mode 100644 index 0000000000000..d018dd8721ee7 --- /dev/null +++ b/packages/kbn-expandable-flyout/src/components/right_section.tsx @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import React from 'react'; +import { RIGHT_SECTION } from './test_ids'; + +interface RightSectionProps { + /** + * Component to be rendered + */ + component: React.ReactElement; + /** + * Width used when rendering the panel + */ + width: number; +} + +/** + * Right section of the expanded flyout rendering a panel + */ +export const RightSection: React.FC = ({ + component, + width, +}: RightSectionProps) => { + return ( + + + {component} + + + ); +}; + +RightSection.displayName = 'RightSection'; diff --git a/packages/kbn-expandable-flyout/src/components/test_ids.ts b/packages/kbn-expandable-flyout/src/components/test_ids.ts new file mode 100644 index 0000000000000..38dd9231712c4 --- /dev/null +++ b/packages/kbn-expandable-flyout/src/components/test_ids.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const RIGHT_SECTION = 'rightSection'; + +export const LEFT_SECTION = 'leftSection'; + +export const PREVIEW_SECTION = 'previewSection'; + +export const PREVIEW_SECTION_CLOSE_BUTTON = 'previewSectionCloseButton'; + +export const PREVIEW_SECTION_BACK_BUTTON = 'previewSectionBackButton'; diff --git a/packages/kbn-expandable-flyout/src/components/translations.ts b/packages/kbn-expandable-flyout/src/components/translations.ts new file mode 100644 index 0000000000000..8b6646fa69df3 --- /dev/null +++ b/packages/kbn-expandable-flyout/src/components/translations.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; + +export const BACK_BUTTON = i18n.translate('expandableFlyout.previewSection.backButton', { + defaultMessage: 'Back', +}); + +export const CLOSE_BUTTON = i18n.translate('expandableFlyout.previewSection.closeButton', { + defaultMessage: 'Close', +}); diff --git a/packages/kbn-expandable-flyout/src/context.tsx b/packages/kbn-expandable-flyout/src/context.tsx new file mode 100644 index 0000000000000..89e8210e9578f --- /dev/null +++ b/packages/kbn-expandable-flyout/src/context.tsx @@ -0,0 +1,172 @@ +/* + * Copyright 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, { createContext, useCallback, useContext, useMemo, useReducer } from 'react'; +import { ActionType } from './actions'; +import { reducer, State } from './reducer'; +import type { FlyoutPanel } from './types'; +import { initialState } from './reducer'; + +export interface ExpandableFlyoutContext { + /** + * Right, left and preview panels + */ + panels: State; + /** + * Open the flyout with left, right and/or preview panels + */ + openFlyout: (panels: { left?: FlyoutPanel; right?: FlyoutPanel; preview?: FlyoutPanel }) => void; + /** + * Replaces the current right panel with a new one + */ + openRightPanel: (panel: FlyoutPanel) => void; + /** + * Replaces the current left panel with a new one + */ + openLeftPanel: (panel: FlyoutPanel) => void; + /** + * Add a new preview panel to the list of current preview panels + */ + openPreviewPanel: (panel: FlyoutPanel) => void; + /** + * Closes right panel + */ + closeRightPanel: () => void; + /** + * Closes left panel + */ + closeLeftPanel: () => void; + /** + * Closes all preview panels + */ + closePreviewPanel: () => void; + /** + * Go back to previous preview panel + */ + previousPreviewPanel: () => void; + /** + * Close all panels and closes flyout + */ + closeFlyout: () => void; +} + +export const ExpandableFlyoutContext = createContext( + undefined +); + +export interface ExpandableFlyoutProviderProps { + /** + * React children + */ + children: React.ReactNode; +} + +/** + * Wrap your plugin with this context for the ExpandableFlyout React component. + */ +export const ExpandableFlyoutProvider = ({ children }: ExpandableFlyoutProviderProps) => { + const [state, dispatch] = useReducer(reducer, initialState); + + const openPanels = useCallback( + ({ + right, + left, + preview, + }: { + right?: FlyoutPanel; + left?: FlyoutPanel; + preview?: FlyoutPanel; + }) => dispatch({ type: ActionType.openFlyout, payload: { left, right, preview } }), + [dispatch] + ); + + const openRightPanel = useCallback( + (panel: FlyoutPanel) => dispatch({ type: ActionType.openRightPanel, payload: panel }), + [dispatch] + ); + + const openLeftPanel = useCallback( + (panel: FlyoutPanel) => dispatch({ type: ActionType.openLeftPanel, payload: panel }), + [dispatch] + ); + + const openPreviewPanel = useCallback( + (panel: FlyoutPanel) => dispatch({ type: ActionType.openPreviewPanel, payload: panel }), + [dispatch] + ); + + const closeRightPanel = useCallback( + () => dispatch({ type: ActionType.closeRightPanel }), + [dispatch] + ); + + const closeLeftPanel = useCallback( + () => dispatch({ type: ActionType.closeLeftPanel }), + [dispatch] + ); + + const closePreviewPanel = useCallback( + () => dispatch({ type: ActionType.closePreviewPanel }), + [dispatch] + ); + + const previousPreviewPanel = useCallback( + () => dispatch({ type: ActionType.previousPreviewPanel }), + [dispatch] + ); + + const closePanels = useCallback(() => dispatch({ type: ActionType.closeFlyout }), [dispatch]); + + const contextValue = useMemo( + () => ({ + panels: state, + openFlyout: openPanels, + openRightPanel, + openLeftPanel, + openPreviewPanel, + closeRightPanel, + closeLeftPanel, + closePreviewPanel, + closeFlyout: closePanels, + previousPreviewPanel, + }), + [ + state, + openPanels, + openRightPanel, + openLeftPanel, + openPreviewPanel, + closeRightPanel, + closeLeftPanel, + closePreviewPanel, + closePanels, + previousPreviewPanel, + ] + ); + + return ( + + {children} + + ); +}; + +/** + * Retrieve context's properties + */ +export const useExpandableFlyoutContext = (): ExpandableFlyoutContext => { + const contextValue = useContext(ExpandableFlyoutContext); + + if (!contextValue) { + throw new Error( + 'ExpandableFlyoutContext can only be used within ExpandableFlyoutContext provider' + ); + } + + return contextValue; +}; diff --git a/packages/kbn-expandable-flyout/src/index.test.tsx b/packages/kbn-expandable-flyout/src/index.test.tsx new file mode 100644 index 0000000000000..fd1d1990ffbad --- /dev/null +++ b/packages/kbn-expandable-flyout/src/index.test.tsx @@ -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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { Panel } from './types'; +import { ExpandableFlyout } from '.'; +import { LEFT_SECTION, PREVIEW_SECTION, RIGHT_SECTION } from './components/test_ids'; +import { ExpandableFlyoutContext } from './context'; + +describe('ExpandableFlyout', () => { + const registeredPanels: Panel[] = [ + { + key: 'key', + width: 500, + component: () =>
    {'component'}
    , + }, + ]; + const onClose = () => window.alert('closed'); + + it(`shouldn't render flyout if no panels`, () => { + const context: ExpandableFlyoutContext = { + panels: { + right: undefined, + left: undefined, + preview: [], + }, + } as unknown as ExpandableFlyoutContext; + + const result = render( + + + + ); + + expect(result.asFragment()).toMatchInlineSnapshot(``); + }); + + it('should render right section', () => { + const context: ExpandableFlyoutContext = { + panels: { + right: { + id: 'key', + }, + left: {}, + preview: [], + }, + } as unknown as ExpandableFlyoutContext; + + const { getByTestId } = render( + + + + ); + + expect(getByTestId(RIGHT_SECTION)).toBeInTheDocument(); + }); + + it('should render left section', () => { + const context: ExpandableFlyoutContext = { + panels: { + right: {}, + left: { + id: 'key', + }, + preview: [], + }, + } as unknown as ExpandableFlyoutContext; + + const { getByTestId } = render( + + + + ); + + expect(getByTestId(LEFT_SECTION)).toBeInTheDocument(); + }); + + it('should render preview section', () => { + const context: ExpandableFlyoutContext = { + panels: { + right: {}, + left: {}, + preview: [ + { + id: 'key', + }, + ], + }, + } as unknown as ExpandableFlyoutContext; + + const { getByTestId } = render( + + + + ); + + expect(getByTestId(PREVIEW_SECTION)).toBeInTheDocument(); + }); +}); diff --git a/packages/kbn-expandable-flyout/src/index.tsx b/packages/kbn-expandable-flyout/src/index.tsx new file mode 100644 index 0000000000000..80dd1d425f2a2 --- /dev/null +++ b/packages/kbn-expandable-flyout/src/index.tsx @@ -0,0 +1,113 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useCallback, useMemo } from 'react'; +import { css } from '@emotion/react'; +import type { EuiFlyoutProps } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlyout } from '@elastic/eui'; +import { useExpandableFlyoutContext } from './context'; +import { PreviewSection } from './components/preview_section'; +import { RightSection } from './components/right_section'; +import type { FlyoutPanel, Panel } from './types'; +import { LeftSection } from './components/left_section'; + +export interface ExpandableFlyoutProps extends EuiFlyoutProps { + /** + * List of all registered panels available for render + */ + registeredPanels: Panel[]; + /** + * Propagate out EuiFlyout onClose event + */ + handleOnFlyoutClosed?: () => void; +} + +/** + * Expandable flyout UI React component. + * Displays 3 sections (right, left, preview) depending on the panels in the context. + */ +export const ExpandableFlyout: React.FC = ({ + registeredPanels, + handleOnFlyoutClosed, + ...flyoutProps +}) => { + const { panels, closeFlyout } = useExpandableFlyoutContext(); + const { left, right, preview } = panels; + + const onClose = useCallback(() => { + if (handleOnFlyoutClosed) handleOnFlyoutClosed(); + closeFlyout(); + }, [closeFlyout, handleOnFlyoutClosed]); + + const leftSection = useMemo( + () => registeredPanels.find((panel) => panel.key === left?.id), + [left, registeredPanels] + ); + + const rightSection = useMemo( + () => registeredPanels.find((panel) => panel.key === right?.id), + [right, registeredPanels] + ); + + // retrieve the last preview panel (most recent) + const mostRecentPreview = preview ? preview[preview.length - 1] : undefined; + const showBackButton = preview && preview.length > 1; + const previewSection = useMemo( + () => registeredPanels.find((panel) => panel.key === mostRecentPreview?.id), + [mostRecentPreview, registeredPanels] + ); + + // do not add the flyout to the dom if there aren't any panels to display + if (!left && !right && !preview.length) { + return <>; + } + + const width: number = (leftSection?.width ?? 0) + (rightSection?.width ?? 0); + + return ( + + + {leftSection && left ? ( + + ) : null} + {rightSection && right ? ( + + ) : null} + + + {previewSection && preview ? ( + + ) : null} + + ); +}; + +ExpandableFlyout.displayName = 'ExpandableFlyout'; diff --git a/packages/kbn-expandable-flyout/src/reducer.test.ts b/packages/kbn-expandable-flyout/src/reducer.test.ts new file mode 100644 index 0000000000000..21128ded7b58e --- /dev/null +++ b/packages/kbn-expandable-flyout/src/reducer.test.ts @@ -0,0 +1,417 @@ +/* + * Copyright 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 { FlyoutPanel } from './types'; +import { initialState, reducer, State } from './reducer'; +import { Action, ActionType } from './actions'; + +const rightPanel1: FlyoutPanel = { + id: 'right1', + path: ['path'], +}; +const leftPanel1: FlyoutPanel = { + id: 'left1', + params: { id: 'id' }, +}; +const previewPanel1: FlyoutPanel = { + id: 'preview1', + state: { id: 'state' }, +}; + +const rightPanel2: FlyoutPanel = { + id: 'right2', + path: ['path'], +}; +const leftPanel2: FlyoutPanel = { + id: 'left2', + params: { id: 'id' }, +}; +const previewPanel2: FlyoutPanel = { + id: 'preview2', + state: { id: 'state' }, +}; +describe('reducer', () => { + describe('should handle openFlyout action', () => { + it('should add panels to empty state', () => { + const state: State = initialState; + const action: Action = { + type: ActionType.openFlyout, + payload: { + right: rightPanel1, + left: leftPanel1, + preview: previewPanel1, + }, + }; + const newState: State = reducer(state, action); + + expect(newState).toEqual({ + left: leftPanel1, + right: rightPanel1, + preview: [previewPanel1], + }); + }); + + it('should override all panels in the state', () => { + const state: State = { + left: leftPanel1, + right: rightPanel1, + preview: [previewPanel1, { id: 'preview' }], + }; + const action: Action = { + type: ActionType.openFlyout, + payload: { + right: rightPanel2, + left: leftPanel2, + preview: previewPanel2, + }, + }; + const newState: State = reducer(state, action); + + expect(newState).toEqual({ + left: leftPanel2, + right: rightPanel2, + preview: [previewPanel2], + }); + }); + + it('should remove all panels despite only passing a single section ', () => { + const state: State = { + left: leftPanel1, + right: rightPanel1, + preview: [previewPanel1], + }; + const action: Action = { + type: ActionType.openFlyout, + payload: { + right: rightPanel2, + }, + }; + const newState: State = reducer(state, action); + + expect(newState).toEqual({ + left: undefined, + right: rightPanel2, + preview: [], + }); + }); + }); + + describe('should handle openRightPanel action', () => { + it('should add right panel to empty state', () => { + const state: State = initialState; + const action: Action = { + type: ActionType.openRightPanel, + payload: rightPanel1, + }; + const newState: State = reducer(state, action); + + expect(newState).toEqual({ + left: undefined, + right: rightPanel1, + preview: [], + }); + }); + + it('should replace right panel', () => { + const state: State = { + left: leftPanel1, + right: rightPanel1, + preview: [previewPanel1], + }; + const action: Action = { + type: ActionType.openRightPanel, + payload: rightPanel2, + }; + const newState: State = reducer(state, action); + + expect(newState).toEqual({ + left: leftPanel1, + right: rightPanel2, + preview: [previewPanel1], + }); + }); + }); + + describe('should handle openLeftPanel action', () => { + it('should add left panel to empty state', () => { + const state: State = initialState; + const action: Action = { + type: ActionType.openLeftPanel, + payload: leftPanel1, + }; + const newState: State = reducer(state, action); + + expect(newState).toEqual({ + left: leftPanel1, + right: undefined, + preview: [], + }); + }); + + it('should replace only left panel', () => { + const state: State = { + left: leftPanel1, + right: rightPanel1, + preview: [previewPanel1], + }; + const action: Action = { + type: ActionType.openLeftPanel, + payload: leftPanel2, + }; + const newState: State = reducer(state, action); + + expect(newState).toEqual({ + left: leftPanel2, + right: rightPanel1, + preview: [previewPanel1], + }); + }); + }); + + describe('should handle openPreviewPanel action', () => { + it('should add preview panel to empty state', () => { + const state: State = initialState; + const action: Action = { + type: ActionType.openPreviewPanel, + payload: previewPanel1, + }; + const newState: State = reducer(state, action); + + expect(newState).toEqual({ + left: undefined, + right: undefined, + preview: [previewPanel1], + }); + }); + + it('should add preview panel to the list of preview panels', () => { + const state: State = { + left: leftPanel1, + right: rightPanel1, + preview: [previewPanel1], + }; + const action: Action = { + type: ActionType.openPreviewPanel, + payload: previewPanel2, + }; + const newState: State = reducer(state, action); + + expect(newState).toEqual({ + left: leftPanel1, + right: rightPanel1, + preview: [previewPanel1, previewPanel2], + }); + }); + }); + + describe('should handle closeRightPanel action', () => { + it('should return empty state when removing right panel from empty state', () => { + const state: State = initialState; + const action: Action = { + type: ActionType.closeRightPanel, + }; + const newState: State = reducer(state, action); + + expect(newState).toEqual(state); + }); + + it(`should return unmodified state when removing right panel when no right panel exist`, () => { + const state: State = { + left: leftPanel1, + right: undefined, + preview: [previewPanel1], + }; + const action: Action = { + type: ActionType.closeRightPanel, + }; + const newState: State = reducer(state, action); + + expect(newState).toEqual(state); + }); + + it('should remove right panel', () => { + const state: State = { + left: leftPanel1, + right: rightPanel1, + preview: [previewPanel1], + }; + const action: Action = { + type: ActionType.closeRightPanel, + }; + const newState: State = reducer(state, action); + + expect(newState).toEqual({ + left: leftPanel1, + right: undefined, + preview: [previewPanel1], + }); + }); + }); + + describe('should handle closeLeftPanel action', () => { + it('should return empty state when removing left panel on empty state', () => { + const state: State = initialState; + const action: Action = { + type: ActionType.closeLeftPanel, + }; + const newState: State = reducer(state, action); + + expect(newState).toEqual(state); + }); + + it(`should return unmodified state when removing left panel when no left panel exist`, () => { + const state: State = { + left: undefined, + right: rightPanel1, + preview: [], + }; + const action: Action = { + type: ActionType.closeLeftPanel, + }; + const newState: State = reducer(state, action); + + expect(newState).toEqual(state); + }); + + it('should remove left panel', () => { + const state: State = { + left: leftPanel1, + right: rightPanel1, + preview: [previewPanel1], + }; + const action: Action = { + type: ActionType.closeLeftPanel, + }; + const newState: State = reducer(state, action); + + expect(newState).toEqual({ + left: undefined, + right: rightPanel1, + preview: [previewPanel1], + }); + }); + }); + + describe('should handle closePreviewPanel action', () => { + it('should return empty state when removing preview panel on empty state', () => { + const state: State = initialState; + const action: Action = { + type: ActionType.closePreviewPanel, + }; + const newState: State = reducer(state, action); + + expect(newState).toEqual(state); + }); + + it(`should return unmodified state when removing preview panel when no preview panel exist`, () => { + const state: State = { + left: leftPanel1, + right: rightPanel1, + preview: [], + }; + const action: Action = { + type: ActionType.closePreviewPanel, + }; + const newState: State = reducer(state, action); + + expect(newState).toEqual(state); + }); + + it('should remove all preview panels', () => { + const state: State = { + left: rightPanel1, + right: leftPanel1, + preview: [previewPanel1, previewPanel2], + }; + const action: Action = { + type: ActionType.closePreviewPanel, + }; + const newState: State = reducer(state, action); + + expect(newState).toEqual({ + left: rightPanel1, + right: leftPanel1, + preview: [], + }); + }); + }); + + describe('should handle previousPreviewPanel action', () => { + it('should return empty state when previous preview panel on an empty state', () => { + const state: State = initialState; + const action: Action = { + type: ActionType.previousPreviewPanel, + }; + const newState: State = reducer(state, action); + + expect(newState).toEqual(state); + }); + + it(`should return unmodified state when previous preview panel when no preview panel exist`, () => { + const state: State = { + left: leftPanel1, + right: rightPanel1, + preview: [], + }; + const action: Action = { + type: ActionType.previousPreviewPanel, + }; + const newState: State = reducer(state, action); + + expect(newState).toEqual(state); + }); + + it('should remove only last preview panel', () => { + const state: State = { + left: leftPanel1, + right: rightPanel1, + preview: [previewPanel1, previewPanel2], + }; + const action: Action = { + type: ActionType.previousPreviewPanel, + }; + const newState: State = reducer(state, action); + + expect(newState).toEqual({ + left: leftPanel1, + right: rightPanel1, + preview: [previewPanel1], + }); + }); + }); + + describe('should handle closeFlyout action', () => { + it('should return empty state when closing flyout on an empty state', () => { + const state: State = initialState; + const action: Action = { + type: ActionType.closeFlyout, + }; + const newState: State = reducer(state, action); + + expect(newState).toEqual(initialState); + }); + + it('should remove all panels', () => { + const state: State = { + left: leftPanel1, + right: rightPanel1, + preview: [previewPanel1], + }; + const action: Action = { + type: ActionType.closeFlyout, + }; + const newState: State = reducer(state, action); + + expect(newState).toEqual({ + left: undefined, + right: undefined, + preview: [], + }); + }); + }); +}); diff --git a/packages/kbn-expandable-flyout/src/reducer.ts b/packages/kbn-expandable-flyout/src/reducer.ts new file mode 100644 index 0000000000000..4901eccfc6bb4 --- /dev/null +++ b/packages/kbn-expandable-flyout/src/reducer.ts @@ -0,0 +1,109 @@ +/* + * Copyright 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 { FlyoutPanel } from './types'; +import { Action, ActionType } from './actions'; + +export interface State { + /** + * Panel to render in the left section + */ + left: FlyoutPanel | undefined; + /** + * Panel to render in the right section + */ + right: FlyoutPanel | undefined; + /** + * Panels to render in the preview section + */ + preview: FlyoutPanel[]; +} + +export const initialState: State = { + left: undefined, + right: undefined, + preview: [], +}; + +export function reducer(state: State, action: Action) { + switch (action.type) { + /** + * Open the flyout by replacing the entire state with new panels. + */ + case ActionType.openFlyout: { + const { left, right, preview } = action.payload; + return { + left, + right, + preview: preview ? [preview] : [], + }; + } + + /** + * Opens a right section by replacing the previous right panel with the new one. + */ + case ActionType.openRightPanel: { + return { ...state, right: action.payload }; + } + + /** + * Opens a left section by replacing the previous left panel with the new one. + */ + case ActionType.openLeftPanel: { + return { ...state, left: action.payload }; + } + + /** + * Opens a preview section by adding to the array of preview panels. + */ + case ActionType.openPreviewPanel: { + return { ...state, preview: [...state.preview, action.payload] }; + } + + /** + * Closes the right section by removing the right panel. + */ + case ActionType.closeRightPanel: { + return { ...state, right: undefined }; + } + + /** + * Close the left section by removing the left panel. + */ + case ActionType.closeLeftPanel: { + return { ...state, left: undefined }; + } + + /** + * Closes the preview section by removing all the preview panels. + */ + case ActionType.closePreviewPanel: { + return { ...state, preview: [] }; + } + + /** + * Navigates to the previous preview panel by removing the last entry in the array of preview panels. + */ + case ActionType.previousPreviewPanel: { + const p: FlyoutPanel[] = [...state.preview]; + p.pop(); + return { ...state, preview: p }; + } + + /** + * Close the flyout by removing all the panels. + */ + case ActionType.closeFlyout: { + return { + left: undefined, + right: undefined, + preview: [], + }; + } + } +} diff --git a/packages/kbn-expandable-flyout/src/types.ts b/packages/kbn-expandable-flyout/src/types.ts new file mode 100644 index 0000000000000..f526832810900 --- /dev/null +++ b/packages/kbn-expandable-flyout/src/types.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; + +export interface FlyoutPanel { + /** + * Unique key to identify the panel + */ + id: string; + /** + * Any parameters necessary for the initial requests within the flyout + */ + params?: Record; + /** + * Tracks the path for what to show in a panel. We may have multiple tabs or details..., so easiest to just use a stack + */ + path?: string[]; + /** + * Tracks visual state such as whether the panel is collapsed + */ + state?: Record; +} + +export interface Panel { + /** + * Unique key used to identify the panel + */ + key?: string; + /** + * Component to be rendered + */ + component: (props: FlyoutPanel) => React.ReactElement; + /** + * Width used when rendering the panel + */ + width: number; // TODO remove this, the width shouldn't be a property of a panel, but handled at the flyout level +} diff --git a/packages/kbn-expandable-flyout/tsconfig.json b/packages/kbn-expandable-flyout/tsconfig.json new file mode 100644 index 0000000000000..d1755389bcddc --- /dev/null +++ b/packages/kbn-expandable-flyout/tsconfig.json @@ -0,0 +1,24 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node", + "react", + "@emotion/react/types/css-prop", + "@testing-library/jest-dom", + "@testing-library/react" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx" + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/i18n" + ] +} diff --git a/packages/kbn-repo-packages/modern/get_git_repo_root.js b/packages/kbn-repo-packages/modern/get_git_repo_root.js new file mode 100644 index 0000000000000..b365b6b8870c9 --- /dev/null +++ b/packages/kbn-repo-packages/modern/get_git_repo_root.js @@ -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 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. + */ + +const Path = require('path'); +const ChildProcess = require('child_process'); + +const cache = new Map(); + +/** + * Synchronously get the git repo root for a given repoRoot and cache the result for the execution + * @param {string} repoRoot + * @returns {string | null} + */ +function getGitRepoRootSync(repoRoot) { + if (cache.has(repoRoot)) { + return cache.get(repoRoot); + } + + try { + const stdout = ChildProcess.execFileSync('git', ['rev-parse', '--show-toplevel'], { + cwd: repoRoot, + encoding: 'utf8', + maxBuffer: Infinity, + }); + + const firstLine = stdout.split('\n')[0]; + const trimPath = firstLine.trim(); + cache.set(repoRoot, Path.basename(trimPath) !== trimPath ? trimPath : null); + + return cache.get(repoRoot); + } catch { + cache.set(repoRoot, null); + return null; + } +} + +module.exports = { getGitRepoRootSync }; diff --git a/packages/kbn-repo-packages/modern/package.js b/packages/kbn-repo-packages/modern/package.js index fc93447ba53dd..d2cd031e75c3d 100644 --- a/packages/kbn-repo-packages/modern/package.js +++ b/packages/kbn-repo-packages/modern/package.js @@ -31,7 +31,7 @@ class Package { * @param {string} path */ static fromManifest(repoRoot, path) { - const manifest = readPackageManifest(path); + const manifest = readPackageManifest(repoRoot, path); const dir = Path.dirname(path); return new Package(repoRoot, dir, manifest, readPackageJson(Path.resolve(dir, 'package.json'))); diff --git a/packages/kbn-repo-packages/modern/parse_package_manifest.js b/packages/kbn-repo-packages/modern/parse_package_manifest.js index 7d51b0b5e51a8..f043fc055de72 100644 --- a/packages/kbn-repo-packages/modern/parse_package_manifest.js +++ b/packages/kbn-repo-packages/modern/parse_package_manifest.js @@ -19,6 +19,7 @@ const { isArrOfStrings, PACKAGE_TYPES, } = require('./parse_helpers'); +const { getGitRepoRootSync } = require('./get_git_repo_root'); const { parse } = require('../utils/jsonc'); const { isValidPluginCategoryInfo, PLUGIN_CATEGORY } = require('./plugin_category_info'); @@ -43,10 +44,11 @@ const isValidOwner = (v) => typeof v === 'string' && v.startsWith('@'); /** * @param {unknown} plugin + * @param {string} repoRoot * @param {string} path * @returns {import('./types').PluginPackageManifest['plugin']} plugin */ -function validatePackageManifestPlugin(plugin, path) { +function validatePackageManifestPlugin(plugin, repoRoot, path) { if (!isObj(plugin)) { throw err('plugin', plugin, 'must be an object'); } @@ -118,7 +120,11 @@ function validatePackageManifestPlugin(plugin, path) { } const segs = path.split(Path.sep); - const isBuild = segs.includes('node_modules') || segs.includes('build'); + const gitRepoRoot = getGitRepoRootSync(repoRoot); + const isBuild = + segs.includes('node_modules') || + (gitRepoRoot && path.startsWith(Path.join(gitRepoRoot, 'build', 'kibana'))); + // TODO: evaluate if __category__ should be removed if (__category__ !== undefined) { if (!isBuild) { throw err( @@ -195,10 +201,11 @@ function validatePackageManifestBuild(build) { /** * Validate the contents of a parsed kibana.jsonc file. * @param {unknown} parsed + * @param {string} repoRoot * @param {string} path * @returns {import('./types').KibanaPackageManifest} */ -function validatePackageManifest(parsed, path) { +function validatePackageManifest(parsed, repoRoot, path) { if (!isObj(parsed)) { throw new Error('expected manifest root to be an object'); } @@ -273,7 +280,7 @@ function validatePackageManifest(parsed, path) { return { type, ...base, - plugin: validatePackageManifestPlugin(plugin, path), + plugin: validatePackageManifestPlugin(plugin, repoRoot, path), }; } @@ -290,9 +297,10 @@ function validatePackageManifest(parsed, path) { /** * Parse a kibana.jsonc file from the filesystem + * @param {string} repoRoot * @param {string} path */ -function readPackageManifest(path) { +function readPackageManifest(repoRoot, path) { let content; try { content = Fs.readFileSync(path, 'utf8'); @@ -313,7 +321,7 @@ function readPackageManifest(path) { throw new Error(`Invalid JSONc: ${error.message}`); } - return validatePackageManifest(parsed, path); + return validatePackageManifest(parsed, repoRoot, path); } catch (error) { throw new Error(`Unable to parse [${path}]: ${error.message}`); } diff --git a/packages/kbn-rule-data-utils/index.ts b/packages/kbn-rule-data-utils/index.ts index 145cc2c5220e9..ea0028b972ed9 100644 --- a/packages/kbn-rule-data-utils/index.ts +++ b/packages/kbn-rule-data-utils/index.ts @@ -7,6 +7,7 @@ */ export * from './src/default_alerts_as_data'; +export * from './src/legacy_alerts_as_data'; export * from './src/technical_field_names'; export * from './src/alerts_as_data_rbac'; export * from './src/alerts_as_data_severity'; diff --git a/packages/kbn-rule-data-utils/src/default_alerts_as_data.ts b/packages/kbn-rule-data-utils/src/default_alerts_as_data.ts index 3a982124b58e6..34b04116b9522 100644 --- a/packages/kbn-rule-data-utils/src/default_alerts_as_data.ts +++ b/packages/kbn-rule-data-utils/src/default_alerts_as_data.ts @@ -8,6 +8,9 @@ import { ValuesType } from 'utility-types'; +const TIMESTAMP = '@timestamp' as const; + +// namespaces const KIBANA_NAMESPACE = 'kibana' as const; const ALERT_NAMESPACE = `${KIBANA_NAMESPACE}.alert` as const; const ALERT_RULE_NAMESPACE = `${ALERT_NAMESPACE}.rule` as const; @@ -21,6 +24,9 @@ const VERSION = `${KIBANA_NAMESPACE}.version` as const; // kibana.alert.action_group - framework action group ID for this alert const ALERT_ACTION_GROUP = `${ALERT_NAMESPACE}.action_group` as const; +// kibana.alert.case_ids - array of cases associated with the alert +const ALERT_CASE_IDS = `${ALERT_NAMESPACE}.case_ids` as const; + // kibana.alert.duration.us - alert duration in nanoseconds - updated each execution // that the alert is active const ALERT_DURATION = `${ALERT_NAMESPACE}.duration.us` as const; @@ -31,8 +37,11 @@ const ALERT_END = `${ALERT_NAMESPACE}.end` as const; // kibana.alert.flapping - whether the alert is currently in a flapping state const ALERT_FLAPPING = `${ALERT_NAMESPACE}.flapping` as const; -// kibana.alert.id - alert ID, also known as alert instance ID -const ALERT_ID = `${ALERT_NAMESPACE}.id` as const; +// kibana.alert.flapping_history - whether the alert is currently in a flapping state +const ALERT_FLAPPING_HISTORY = `${ALERT_NAMESPACE}.flapping_history` as const; + +// kibana.alert.instance.id - alert ID, also known as alert instance ID +const ALERT_INSTANCE_ID = `${ALERT_NAMESPACE}.instance.id` as const; // kibana.alert.last_detected - timestamp when the alert was last seen const ALERT_LAST_DETECTED = `${ALERT_NAMESPACE}.last_detected` as const; @@ -90,10 +99,12 @@ const namespaces = { const fields = { ALERT_ACTION_GROUP, + ALERT_CASE_IDS, ALERT_DURATION, ALERT_END, ALERT_FLAPPING, - ALERT_ID, + ALERT_FLAPPING_HISTORY, + ALERT_INSTANCE_ID, ALERT_LAST_DETECTED, ALERT_REASON, ALERT_RULE_CATEGORY, @@ -111,15 +122,24 @@ const fields = { ALERT_UUID, ALERT_WORKFLOW_STATUS, SPACE_IDS, + TIMESTAMP, VERSION, }; export { + // namespaces + ALERT_NAMESPACE, + ALERT_RULE_NAMESPACE, + KIBANA_NAMESPACE, + + // fields ALERT_ACTION_GROUP, + ALERT_CASE_IDS, ALERT_DURATION, ALERT_END, ALERT_FLAPPING, - ALERT_ID, + ALERT_FLAPPING_HISTORY, + ALERT_INSTANCE_ID, ALERT_LAST_DETECTED, ALERT_REASON, ALERT_RULE_CATEGORY, @@ -137,10 +157,8 @@ export { ALERT_UUID, ALERT_WORKFLOW_STATUS, SPACE_IDS, + TIMESTAMP, VERSION, - ALERT_NAMESPACE, - ALERT_RULE_NAMESPACE, - KIBANA_NAMESPACE, }; export type DefaultAlertFieldName = ValuesType; diff --git a/packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts b/packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts new file mode 100644 index 0000000000000..4dd6c2be0c2a6 --- /dev/null +++ b/packages/kbn-rule-data-utils/src/legacy_alerts_as_data.ts @@ -0,0 +1,84 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ALERT_NAMESPACE, ALERT_RULE_NAMESPACE } from './default_alerts_as_data'; + +const ECS_VERSION = 'ecs.version' as const; +const EVENT_ACTION = 'event.action' as const; +const EVENT_KIND = 'event.kind' as const; +const TAGS = 'tags' as const; + +// These are the fields that are in the rule registry technical component template +// that are NOT in the framework alerts as data common component template + +// We will maintain a legacy component template that can be used by legacy +// rule registry rules with these fields. +const ALERT_RISK_SCORE = `${ALERT_NAMESPACE}.risk_score` as const; +const ALERT_RULE_AUTHOR = `${ALERT_RULE_NAMESPACE}.author` as const; +const ALERT_RULE_CREATED_AT = `${ALERT_RULE_NAMESPACE}.created_at` as const; +const ALERT_RULE_CREATED_BY = `${ALERT_RULE_NAMESPACE}.created_by` as const; +const ALERT_RULE_DESCRIPTION = `${ALERT_RULE_NAMESPACE}.description` as const; +const ALERT_RULE_ENABLED = `${ALERT_RULE_NAMESPACE}.enabled` as const; +const ALERT_RULE_FROM = `${ALERT_RULE_NAMESPACE}.from` as const; +const ALERT_RULE_INTERVAL = `${ALERT_RULE_NAMESPACE}.interval` as const; +const ALERT_RULE_LICENSE = `${ALERT_RULE_NAMESPACE}.license` as const; +const ALERT_RULE_NOTE = `${ALERT_RULE_NAMESPACE}.note` as const; +const ALERT_RULE_REFERENCES = `${ALERT_RULE_NAMESPACE}.references` as const; +const ALERT_RULE_RULE_ID = `${ALERT_RULE_NAMESPACE}.rule_id` as const; +const ALERT_RULE_RULE_NAME_OVERRIDE = `${ALERT_RULE_NAMESPACE}.rule_name_override` as const; +const ALERT_RULE_TO = `${ALERT_RULE_NAMESPACE}.to` as const; +const ALERT_RULE_TYPE = `${ALERT_RULE_NAMESPACE}.type` as const; +const ALERT_RULE_UPDATED_AT = `${ALERT_RULE_NAMESPACE}.updated_at` as const; +const ALERT_RULE_UPDATED_BY = `${ALERT_RULE_NAMESPACE}.updated_by` as const; +const ALERT_RULE_VERSION = `${ALERT_RULE_NAMESPACE}.version` as const; +const ALERT_SEVERITY = `${ALERT_NAMESPACE}.severity` as const; +const ALERT_SUPPRESSION_META = `${ALERT_NAMESPACE}.suppression` as const; +const ALERT_SUPPRESSION_TERMS = `${ALERT_SUPPRESSION_META}.terms` as const; +const ALERT_SUPPRESSION_FIELD = `${ALERT_SUPPRESSION_TERMS}.field` as const; +const ALERT_SUPPRESSION_VALUE = `${ALERT_SUPPRESSION_TERMS}.value` as const; +const ALERT_SUPPRESSION_START = `${ALERT_SUPPRESSION_META}.start` as const; +const ALERT_SUPPRESSION_END = `${ALERT_SUPPRESSION_META}.end` as const; +const ALERT_SUPPRESSION_DOCS_COUNT = `${ALERT_SUPPRESSION_META}.docs_count` as const; +const ALERT_SYSTEM_STATUS = `${ALERT_NAMESPACE}.system_status` as const; +const ALERT_WORKFLOW_REASON = `${ALERT_NAMESPACE}.workflow_reason` as const; +const ALERT_WORKFLOW_USER = `${ALERT_NAMESPACE}.workflow_user` as const; + +export { + ALERT_RISK_SCORE, + ALERT_RULE_AUTHOR, + ALERT_RULE_CREATED_AT, + ALERT_RULE_CREATED_BY, + ALERT_RULE_DESCRIPTION, + ALERT_RULE_ENABLED, + ALERT_RULE_FROM, + ALERT_RULE_INTERVAL, + ALERT_RULE_LICENSE, + ALERT_RULE_NOTE, + ALERT_RULE_REFERENCES, + ALERT_RULE_RULE_ID, + ALERT_RULE_RULE_NAME_OVERRIDE, + ALERT_RULE_TO, + ALERT_RULE_TYPE, + ALERT_RULE_UPDATED_AT, + ALERT_RULE_UPDATED_BY, + ALERT_RULE_VERSION, + ALERT_SEVERITY, + ALERT_SUPPRESSION_DOCS_COUNT, + ALERT_SUPPRESSION_END, + ALERT_SUPPRESSION_FIELD, + ALERT_SUPPRESSION_START, + ALERT_SUPPRESSION_TERMS, + ALERT_SUPPRESSION_VALUE, + ALERT_SYSTEM_STATUS, + ALERT_WORKFLOW_REASON, + ALERT_WORKFLOW_USER, + ECS_VERSION, + EVENT_ACTION, + EVENT_KIND, + TAGS, +}; diff --git a/packages/kbn-rule-data-utils/src/technical_field_names.ts b/packages/kbn-rule-data-utils/src/technical_field_names.ts index 89eca0f923046..cf45162b20853 100644 --- a/packages/kbn-rule-data-utils/src/technical_field_names.ts +++ b/packages/kbn-rule-data-utils/src/technical_field_names.ts @@ -8,11 +8,15 @@ import { ValuesType } from 'utility-types'; import { + ALERT_NAMESPACE, + ALERT_RULE_NAMESPACE, KIBANA_NAMESPACE, ALERT_ACTION_GROUP, + ALERT_CASE_IDS, ALERT_DURATION, ALERT_END, ALERT_FLAPPING, + ALERT_INSTANCE_ID, ALERT_REASON, ALERT_RULE_CATEGORY, ALERT_RULE_CONSUMER, @@ -29,61 +33,61 @@ import { ALERT_UUID, ALERT_WORKFLOW_STATUS, SPACE_IDS, + TIMESTAMP, VERSION, - ALERT_NAMESPACE, - ALERT_RULE_NAMESPACE, } from './default_alerts_as_data'; +import { + ALERT_RISK_SCORE, + ALERT_RULE_AUTHOR, + ALERT_RULE_CREATED_AT, + ALERT_RULE_CREATED_BY, + ALERT_RULE_DESCRIPTION, + ALERT_RULE_ENABLED, + ALERT_RULE_FROM, + ALERT_RULE_INTERVAL, + ALERT_RULE_LICENSE, + ALERT_RULE_NOTE, + ALERT_RULE_REFERENCES, + ALERT_RULE_RULE_ID, + ALERT_RULE_RULE_NAME_OVERRIDE, + ALERT_RULE_TO, + ALERT_RULE_TYPE, + ALERT_RULE_UPDATED_AT, + ALERT_RULE_UPDATED_BY, + ALERT_RULE_VERSION, + ALERT_SEVERITY, + ALERT_SUPPRESSION_DOCS_COUNT, + ALERT_SUPPRESSION_END, + ALERT_SUPPRESSION_FIELD, + ALERT_SUPPRESSION_START, + ALERT_SUPPRESSION_TERMS, + ALERT_SUPPRESSION_VALUE, + ALERT_SYSTEM_STATUS, + ALERT_WORKFLOW_REASON, + ALERT_WORKFLOW_USER, + ECS_VERSION, + EVENT_ACTION, + EVENT_KIND, + TAGS, +} from './legacy_alerts_as_data'; + +// The following fields were identified as technical field names but were not defined in the +// rule registry technical component template. We will leave these here for backwards +// compatibility but these consts should be moved to the plugin that uses them + const ALERT_RULE_THREAT_NAMESPACE = `${ALERT_RULE_NAMESPACE}.threat` as const; -const ECS_VERSION = 'ecs.version' as const; -const EVENT_ACTION = 'event.action' as const; -const EVENT_KIND = 'event.kind' as const; const EVENT_MODULE = 'event.module' as const; -const TAGS = 'tags' as const; -const TIMESTAMP = '@timestamp' as const; // Fields pertaining to the alert const ALERT_BUILDING_BLOCK_TYPE = `${ALERT_NAMESPACE}.building_block_type` as const; const ALERT_EVALUATION_THRESHOLD = `${ALERT_NAMESPACE}.evaluation.threshold` as const; const ALERT_EVALUATION_VALUE = `${ALERT_NAMESPACE}.evaluation.value` as const; -const ALERT_INSTANCE_ID = `${ALERT_NAMESPACE}.instance.id` as const; -const ALERT_RISK_SCORE = `${ALERT_NAMESPACE}.risk_score` as const; -const ALERT_SEVERITY = `${ALERT_NAMESPACE}.severity` as const; -const ALERT_SYSTEM_STATUS = `${ALERT_NAMESPACE}.system_status` as const; -const ALERT_WORKFLOW_REASON = `${ALERT_NAMESPACE}.workflow_reason` as const; -const ALERT_WORKFLOW_USER = `${ALERT_NAMESPACE}.workflow_user` as const; -const ALERT_SUPPRESSION_META = `${ALERT_NAMESPACE}.suppression` as const; -const ALERT_SUPPRESSION_TERMS = `${ALERT_SUPPRESSION_META}.terms` as const; -const ALERT_SUPPRESSION_FIELD = `${ALERT_SUPPRESSION_TERMS}.field` as const; -const ALERT_SUPPRESSION_VALUE = `${ALERT_SUPPRESSION_TERMS}.value` as const; -const ALERT_SUPPRESSION_START = `${ALERT_SUPPRESSION_META}.start` as const; -const ALERT_SUPPRESSION_END = `${ALERT_SUPPRESSION_META}.end` as const; -const ALERT_SUPPRESSION_DOCS_COUNT = `${ALERT_SUPPRESSION_META}.docs_count` as const; - -// Fields pertaining to the cases associated with the alert -const ALERT_CASE_IDS = `${ALERT_NAMESPACE}.case_ids` as const; // Fields pertaining to the rule associated with the alert -const ALERT_RULE_AUTHOR = `${ALERT_RULE_NAMESPACE}.author` as const; -const ALERT_RULE_CREATED_AT = `${ALERT_RULE_NAMESPACE}.created_at` as const; -const ALERT_RULE_CREATED_BY = `${ALERT_RULE_NAMESPACE}.created_by` as const; -const ALERT_RULE_DESCRIPTION = `${ALERT_RULE_NAMESPACE}.description` as const; -const ALERT_RULE_ENABLED = `${ALERT_RULE_NAMESPACE}.enabled` as const; const ALERT_RULE_EXCEPTIONS_LIST = `${ALERT_RULE_NAMESPACE}.exceptions_list` as const; -const ALERT_RULE_FROM = `${ALERT_RULE_NAMESPACE}.from` as const; -const ALERT_RULE_INTERVAL = `${ALERT_RULE_NAMESPACE}.interval` as const; -const ALERT_RULE_LICENSE = `${ALERT_RULE_NAMESPACE}.license` as const; const ALERT_RULE_NAMESPACE_FIELD = `${ALERT_RULE_NAMESPACE}.namespace` as const; -const ALERT_RULE_NOTE = `${ALERT_RULE_NAMESPACE}.note` as const; -const ALERT_RULE_REFERENCES = `${ALERT_RULE_NAMESPACE}.references` as const; -const ALERT_RULE_RULE_ID = `${ALERT_RULE_NAMESPACE}.rule_id` as const; -const ALERT_RULE_RULE_NAME_OVERRIDE = `${ALERT_RULE_NAMESPACE}.rule_name_override` as const; -const ALERT_RULE_TO = `${ALERT_RULE_NAMESPACE}.to` as const; -const ALERT_RULE_TYPE = `${ALERT_RULE_NAMESPACE}.type` as const; -const ALERT_RULE_UPDATED_AT = `${ALERT_RULE_NAMESPACE}.updated_at` as const; -const ALERT_RULE_UPDATED_BY = `${ALERT_RULE_NAMESPACE}.updated_by` as const; -const ALERT_RULE_VERSION = `${ALERT_RULE_NAMESPACE}.version` as const; // Fields pertaining to the threat tactic associated with the rule const ALERT_THREAT_FRAMEWORK = `${ALERT_RULE_THREAT_NAMESPACE}.framework` as const; @@ -186,36 +190,8 @@ export { ALERT_BUILDING_BLOCK_TYPE, ALERT_EVALUATION_THRESHOLD, ALERT_EVALUATION_VALUE, - ALERT_INSTANCE_ID, - ALERT_RISK_SCORE, - ALERT_WORKFLOW_REASON, - ALERT_WORKFLOW_USER, - ALERT_CASE_IDS, - ALERT_RULE_AUTHOR, - ALERT_RULE_CREATED_AT, - ALERT_RULE_CREATED_BY, - ALERT_RULE_DESCRIPTION, - ALERT_RULE_ENABLED, ALERT_RULE_EXCEPTIONS_LIST, - ALERT_RULE_FROM, - ALERT_RULE_INTERVAL, - ALERT_RULE_LICENSE, ALERT_RULE_NAMESPACE_FIELD, - ALERT_RULE_NOTE, - ALERT_RULE_REFERENCES, - ALERT_RULE_RULE_ID, - ALERT_RULE_RULE_NAME_OVERRIDE, - ALERT_RULE_TO, - ALERT_RULE_TYPE, - ALERT_RULE_UPDATED_AT, - ALERT_RULE_UPDATED_BY, - ALERT_RULE_VERSION, - ALERT_SEVERITY, - ALERT_SYSTEM_STATUS, - ECS_VERSION, - EVENT_ACTION, - EVENT_KIND, - EVENT_MODULE, ALERT_THREAT_FRAMEWORK, ALERT_THREAT_TACTIC_ID, ALERT_THREAT_TACTIC_NAME, @@ -226,14 +202,7 @@ export { ALERT_THREAT_TECHNIQUE_SUBTECHNIQUE_ID, ALERT_THREAT_TECHNIQUE_SUBTECHNIQUE_NAME, ALERT_THREAT_TECHNIQUE_SUBTECHNIQUE_REFERENCE, - ALERT_SUPPRESSION_TERMS, - ALERT_SUPPRESSION_FIELD, - ALERT_SUPPRESSION_VALUE, - ALERT_SUPPRESSION_START, - ALERT_SUPPRESSION_END, - ALERT_SUPPRESSION_DOCS_COUNT, - TAGS, - TIMESTAMP, + EVENT_MODULE, }; export type TechnicalRuleDataFieldName = ValuesType; diff --git a/packages/kbn-rule-data-utils/tsconfig.json b/packages/kbn-rule-data-utils/tsconfig.json index 5c94013fc2eaf..77352c4f44209 100644 --- a/packages/kbn-rule-data-utils/tsconfig.json +++ b/packages/kbn-rule-data-utils/tsconfig.json @@ -11,7 +11,7 @@ "**/*.ts" ], "kbn_references": [ - "@kbn/es-query" + "@kbn/es-query", ], "exclude": [ "target/**/*", diff --git a/packages/shared-ux/button_toolbar/index.ts b/packages/shared-ux/button_toolbar/index.ts index 4e570029d7927..e290df34a01b3 100644 --- a/packages/shared-ux/button_toolbar/index.ts +++ b/packages/shared-ux/button_toolbar/index.ts @@ -6,12 +6,12 @@ * Side Public License, v 1. */ -export { PrimaryButton, IconButtonGroup, AddFromLibraryButton } from './src/buttons'; +export { ToolbarButton, IconButtonGroup, AddFromLibraryButton } from './src/buttons'; export type { AddFromLibraryButtonProps, IconButtonGroupProps, + ToolbarButtonProps, IconButton, - PrimaryButtonProps, } from './src/buttons'; export { ToolbarPopover } from './src/popover'; @@ -19,4 +19,4 @@ export type { ToolbarPopoverProps } from './src/popover'; export { Toolbar } from './src/toolbar'; export type { ToolbarProps } from './src/toolbar'; -export type { ToolbarButton } from './src/toolbar'; +export type { ToolbarButtonType } from './src/toolbar'; diff --git a/packages/shared-ux/button_toolbar/src/buttons/add_from_library/__snapshots__/add_from_library.test.tsx.snap b/packages/shared-ux/button_toolbar/src/buttons/add_from_library/__snapshots__/add_from_library.test.tsx.snap index 46ed9f7f6f769..a9e5ff8226fa9 100644 --- a/packages/shared-ux/button_toolbar/src/buttons/add_from_library/__snapshots__/add_from_library.test.tsx.snap +++ b/packages/shared-ux/button_toolbar/src/buttons/add_from_library/__snapshots__/add_from_library.test.tsx.snap @@ -1,8 +1,8 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[` is rendered 1`] = ` +exports[` is rendered 1`] = ` -`; diff --git a/packages/shared-ux/button_toolbar/src/buttons/primary/primary.tsx b/packages/shared-ux/button_toolbar/src/buttons/primary/primary.tsx deleted file mode 100644 index 4d2975db2682b..0000000000000 --- a/packages/shared-ux/button_toolbar/src/buttons/primary/primary.tsx +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React from 'react'; -import { EuiButton } from '@elastic/eui'; -import { EuiButtonPropsForButton } from '@elastic/eui/src/components/button/button'; - -/** - * Props for `PrimaryButton`. - */ -export interface Props extends Pick { - label: string; -} - -/** - * A primary action button, usually appearing first in the toolbar. - */ -export const PrimaryButton = ({ label, iconSide = 'left', ...rest }: Props) => { - return ( - - {label} - - ); -}; diff --git a/packages/shared-ux/button_toolbar/src/buttons/toolbar_button/__snapshots__/toolbar_button.test.tsx.snap b/packages/shared-ux/button_toolbar/src/buttons/toolbar_button/__snapshots__/toolbar_button.test.tsx.snap new file mode 100644 index 0000000000000..e39fd8be3a02b --- /dev/null +++ b/packages/shared-ux/button_toolbar/src/buttons/toolbar_button/__snapshots__/toolbar_button.test.tsx.snap @@ -0,0 +1,37 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` is rendered - default 1`] = ` + +`; + +exports[` is rendered - primary 1`] = ` + +`; diff --git a/packages/shared-ux/button_toolbar/src/buttons/toolbar_button/index.ts b/packages/shared-ux/button_toolbar/src/buttons/toolbar_button/index.ts new file mode 100644 index 0000000000000..335aa2bc4fe41 --- /dev/null +++ b/packages/shared-ux/button_toolbar/src/buttons/toolbar_button/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { ToolbarButton } from './toolbar_button'; +export type { Props as ToolbarButtonProps } from './toolbar_button'; diff --git a/packages/shared-ux/button_toolbar/src/buttons/primary/primary.stories.tsx b/packages/shared-ux/button_toolbar/src/buttons/toolbar_button/toolbar_button.stories.tsx similarity index 54% rename from packages/shared-ux/button_toolbar/src/buttons/primary/primary.stories.tsx rename to packages/shared-ux/button_toolbar/src/buttons/toolbar_button/toolbar_button.stories.tsx index fb3b53b160f4c..580d894ecd042 100644 --- a/packages/shared-ux/button_toolbar/src/buttons/primary/primary.stories.tsx +++ b/packages/shared-ux/button_toolbar/src/buttons/toolbar_button/toolbar_button.stories.tsx @@ -7,37 +7,42 @@ */ import React from 'react'; - -import { PrimaryButton as Component } from './primary'; +import { ToolbarButton as Component } from './toolbar_button'; import mdx from '../../../README.mdx'; -const argTypes = { - iconType: { - control: { - type: 'radio', - expanded: true, - options: ['apps', 'logoGithub', 'folderCheck', 'documents'], - }, - }, -}; - -type Params = Record; - export default { title: 'Button Toolbar/Buttons', - description: 'A primary button that is a part of a toolbar.', + description: 'A button that is a part of a toolbar.', parameters: { docs: { page: mdx, }, }, - argTypes, }; -export const PrimaryButton = ({ iconType }: Params) => { - return ; +const argTypes = { + buttonType: { + defaultValue: 'empty', + control: { + type: 'radio', + options: ['empty', 'primary'], + }, + }, + iconSide: { + defaultValue: 'left', + control: { + type: 'radio', + options: ['left', 'right', 'undefined'], + }, + }, }; -PrimaryButton.args = { - iconType: 'apps', +type Params = Record; + +export const ToolbarButton = ({ buttonType, iconSide }: Params) => { + return ( + + ); }; + +ToolbarButton.argTypes = argTypes; diff --git a/packages/shared-ux/button_toolbar/src/buttons/toolbar_button/toolbar_button.styles.ts b/packages/shared-ux/button_toolbar/src/buttons/toolbar_button/toolbar_button.styles.ts new file mode 100644 index 0000000000000..162e81bb4efcb --- /dev/null +++ b/packages/shared-ux/button_toolbar/src/buttons/toolbar_button/toolbar_button.styles.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { UseEuiTheme } from '@elastic/eui'; + +export const ToolbarButtonStyles = ({ euiTheme }: UseEuiTheme) => { + return { + emptyButton: { + backgroundColor: euiTheme.colors.emptyShade, + border: `${euiTheme.border.thin} !important`, + color: `${euiTheme.colors.text}`, + }, + }; +}; diff --git a/packages/shared-ux/button_toolbar/src/buttons/primary/primary.test.tsx b/packages/shared-ux/button_toolbar/src/buttons/toolbar_button/toolbar_button.test.tsx similarity index 54% rename from packages/shared-ux/button_toolbar/src/buttons/primary/primary.test.tsx rename to packages/shared-ux/button_toolbar/src/buttons/toolbar_button/toolbar_button.test.tsx index 8478ca1842c41..74b5fde37f5ff 100644 --- a/packages/shared-ux/button_toolbar/src/buttons/primary/primary.test.tsx +++ b/packages/shared-ux/button_toolbar/src/buttons/toolbar_button/toolbar_button.test.tsx @@ -9,17 +9,24 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test-jest-helpers'; -import { PrimaryButton } from './primary'; +import { ToolbarButton } from './toolbar_button'; describe('', () => { - test('is rendered', () => { - const component = mountWithIntl(); + test('is rendered - default', () => { + const component = mountWithIntl( 'click'} />); expect(component.render()).toMatchSnapshot(); }); - test('it can be passed a functional onClick handler', () => { + test('is rendered - primary', () => { + const component = mountWithIntl( + 'click'} /> + ); + expect(component.render()).toMatchSnapshot(); + }); + + test('accepts an onClick handler', () => { const mockHandler = jest.fn(); - const component = mountWithIntl(); + const component = mountWithIntl(); component.find('button').simulate('click'); expect(mockHandler).toHaveBeenCalled(); }); diff --git a/packages/shared-ux/button_toolbar/src/buttons/toolbar_button/toolbar_button.tsx b/packages/shared-ux/button_toolbar/src/buttons/toolbar_button/toolbar_button.tsx new file mode 100644 index 0000000000000..3d04e689e4820 --- /dev/null +++ b/packages/shared-ux/button_toolbar/src/buttons/toolbar_button/toolbar_button.tsx @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { EuiButton, useEuiTheme } from '@elastic/eui'; +import { EuiButtonPropsForButton } from '@elastic/eui/src/components/button/button'; + +import { ToolbarButtonStyles } from './toolbar_button.styles'; + +type ToolbarButtonTypes = 'primary' | 'empty'; + +/** + * Props for `PrimaryButton`. + */ +export interface Props + extends Pick { + label: string; + type?: ToolbarButtonTypes; +} + +export const ToolbarButton: React.FunctionComponent = ({ + label, + type = 'empty', + iconSide = 'left', + ...rest +}) => { + const euiTheme = useEuiTheme(); + const toolbarButtonStyleProps: EuiButtonPropsForButton = + type === 'primary' + ? { color: 'primary', fill: true } + : { color: 'text', css: ToolbarButtonStyles(euiTheme).emptyButton }; + + return ( + + {label} + + ); +}; diff --git a/packages/shared-ux/button_toolbar/src/popover/__snapshots__/popover.test.tsx.snap b/packages/shared-ux/button_toolbar/src/popover/__snapshots__/popover.test.tsx.snap index 6129d515e3a7f..9b4ac4a0ddac6 100644 --- a/packages/shared-ux/button_toolbar/src/popover/__snapshots__/popover.test.tsx.snap +++ b/packages/shared-ux/button_toolbar/src/popover/__snapshots__/popover.test.tsx.snap @@ -9,7 +9,7 @@ exports[` is rendered 1`] = ` class="euiPopover__anchor css-16vtueo-render" >
    diff --git a/packages/shared-ux/button_toolbar/src/popover/popover.stories.tsx b/packages/shared-ux/button_toolbar/src/popover/popover.stories.tsx index e0c37b035cbd9..f59dbc242dd74 100644 --- a/packages/shared-ux/button_toolbar/src/popover/popover.stories.tsx +++ b/packages/shared-ux/button_toolbar/src/popover/popover.stories.tsx @@ -14,10 +14,17 @@ import { ToolbarPopover as Component } from './popover'; import mdx from '../../README.mdx'; const argTypes = { - iconSide: { + showIcon: { + defaultValue: true, + control: { + type: 'boolean', + }, + }, + buttonType: { + defaultValue: 'empty', control: { type: 'radio', - options: ['left', 'right', 'undefined'], + options: ['empty', 'primary'], }, }, }; @@ -35,12 +42,12 @@ export default { argTypes, }; -export const Popover = ({ iconSide }: Params) => { +export const Popover = ({ showIcon, buttonType }: Params) => { return ( {() => ( @@ -72,6 +79,4 @@ export const Popover = ({ iconSide }: Params) => { ); }; -Popover.args = { - iconSide: 'left', -}; +Popover.argTypes = argTypes; diff --git a/packages/shared-ux/button_toolbar/src/popover/popover.test.tsx b/packages/shared-ux/button_toolbar/src/popover/popover.test.tsx index afdf0007f8883..b03cb9e63d460 100644 --- a/packages/shared-ux/button_toolbar/src/popover/popover.test.tsx +++ b/packages/shared-ux/button_toolbar/src/popover/popover.test.tsx @@ -15,7 +15,6 @@ describe('', () => { test('is rendered', () => { const isOpen = true; const component = mountWithIntl( !isOpen} />); - expect(component.render()).toMatchSnapshot(); }); @@ -30,4 +29,45 @@ describe('', () => { component.simulate('click'); expect(mockHandler).toHaveBeenCalled(); }); + + test('defaults to a bordered empty button', () => { + const isOpen = true; + const component = mountWithIntl( !isOpen} />); + const button = component.find('EuiButton'); + expect(button.prop('color')).toBe('text'); + expect(button.prop('css')).toMatchObject({ + backgroundColor: '#FFF', + border: '1px solid #D3DAE6 !important', + color: '#343741', + }); + }); + + test('accepts a button type', () => { + const isOpen = true; + const component = mountWithIntl( + !isOpen} /> + ); + const button = component.find('EuiButton'); + expect(button.prop('color')).toBe('primary'); + }); + + test('if not given an iconType, render arrowDown on the right', () => { + const isOpen = false; + + const component = mountWithIntl( !isOpen} />); + const button = component.find('EuiButton'); + expect(button.prop('iconType')).toBe('arrowDown'); + expect(button.prop('iconSide')).toBe('right'); + }); + + test('if given an iconType, render it on the left', () => { + const isOpen = false; + + const component = mountWithIntl( + !isOpen} /> + ); + const button = component.find('EuiButton'); + expect(button.prop('iconType')).toBe('plusInCircle'); + expect(button.prop('iconSide')).toBe('left'); + }); }); diff --git a/packages/shared-ux/button_toolbar/src/popover/popover.tsx b/packages/shared-ux/button_toolbar/src/popover/popover.tsx index fdf60c1f0d5fa..7e7f6c24e2174 100644 --- a/packages/shared-ux/button_toolbar/src/popover/popover.tsx +++ b/packages/shared-ux/button_toolbar/src/popover/popover.tsx @@ -10,9 +10,9 @@ import React, { useState } from 'react'; import { EuiPopover } from '@elastic/eui'; import { Props as EuiPopoverProps } from '@elastic/eui/src/components/popover/popover'; -import { PrimaryButton, Props as ButtonProps } from '../buttons/primary/primary'; +import { ToolbarButtonProps, ToolbarButton } from '../buttons'; -type AllowedButtonProps = Omit; +type AllowedButtonProps = Omit; type AllowedPopoverProps = Omit< EuiPopoverProps, 'button' | 'isOpen' | 'closePopover' | 'anchorPosition' @@ -29,13 +29,18 @@ export type Props = AllowedButtonProps & /** * A button which opens a popover of additional actions within the toolbar. */ -export const ToolbarPopover = ({ label, iconType, children, iconSide, ...popover }: Props) => { +export const ToolbarPopover = ({ type, label, iconType, children, ...popover }: Props) => { const [isOpen, setIsOpen] = useState(false); const onButtonClick = () => setIsOpen((status) => !status); const closePopover = () => setIsOpen(false); - const button = ; + const button = ( + + ); return ( // the following ts-ignore is needed until typings/* directory is exposed for consumption to packages diff --git a/packages/shared-ux/button_toolbar/src/toolbar/index.ts b/packages/shared-ux/button_toolbar/src/toolbar/index.ts index b0e09e92f1528..99db9450dd4ba 100644 --- a/packages/shared-ux/button_toolbar/src/toolbar/index.ts +++ b/packages/shared-ux/button_toolbar/src/toolbar/index.ts @@ -10,4 +10,4 @@ export { Toolbar } from './toolbar'; export type { Props as ToolbarProps } from './toolbar'; -export type { ToolbarButton } from './toolbar'; +export type { ToolbarButtonType } from './toolbar'; diff --git a/packages/shared-ux/button_toolbar/src/toolbar/toolbar.stories.tsx b/packages/shared-ux/button_toolbar/src/toolbar/toolbar.stories.tsx index 61bcf4bb4153f..34fb09227b04c 100644 --- a/packages/shared-ux/button_toolbar/src/toolbar/toolbar.stories.tsx +++ b/packages/shared-ux/button_toolbar/src/toolbar/toolbar.stories.tsx @@ -12,7 +12,7 @@ import { action } from '@storybook/addon-actions'; import { EuiContextMenu } from '@elastic/eui'; import { Toolbar } from './toolbar'; -import { AddFromLibraryButton, IconButtonGroup, PrimaryButton } from '../buttons'; +import { AddFromLibraryButton, IconButtonGroup, ToolbarButton } from '../buttons'; import { ToolbarPopover } from '../popover'; const iconButtons = [ @@ -45,9 +45,21 @@ const iconButtons = [ ]; const primaryButtonConfigs = { - Generic: , + Generic: ( + + ), Canvas: ( - + {() => ( ), Dashboard: ( - + ), }; @@ -96,14 +116,17 @@ const extraButtonConfigs = { { name: 'Lens', icon: 'lensApp', + onClick: action('Lens'), }, { name: 'Maps', icon: 'logoMaps', + onClick: action('Maps'), }, { name: 'TSVB', icon: 'visVisualBuilder', + onClick: action('TSVB'), }, ], }, @@ -156,9 +179,13 @@ const Template: Story<{ showAddFromLibraryButton: boolean; }> = ({ iconButtonCount, solution, showAddFromLibraryButton }) => { const primaryButton = primaryButtonConfigs[solution]; - const extraButtons = extraButtonConfigs[solution]; + const extraButtons = showAddFromLibraryButton + ? [ + ...(extraButtonConfigs[solution] ?? []), + , + ] + : extraButtonConfigs[solution]; let iconButtonGroup; - let addFromLibraryButton; if (iconButtonCount > 0) { iconButtonGroup = ( @@ -166,17 +193,12 @@ const Template: Story<{ ); } - if (showAddFromLibraryButton) { - addFromLibraryButton = ; - } - return ( {{ primaryButton, iconButtonGroup, extraButtons, - addFromLibraryButton, }} ); diff --git a/packages/shared-ux/button_toolbar/src/toolbar/toolbar.test.tsx b/packages/shared-ux/button_toolbar/src/toolbar/toolbar.test.tsx index 729695edab5e8..9aa561af61781 100644 --- a/packages/shared-ux/button_toolbar/src/toolbar/toolbar.test.tsx +++ b/packages/shared-ux/button_toolbar/src/toolbar/toolbar.test.tsx @@ -10,11 +10,13 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { Toolbar } from './toolbar'; -import { PrimaryButton } from '../buttons'; +import { ToolbarButton } from '../buttons'; describe('', () => { test('is rendered', () => { - const primaryButton = 'click'} />; + const primaryButton = ( + 'click'} /> + ); const children = { primaryButton }; const component = mountWithIntl(); @@ -23,7 +25,9 @@ describe('', () => { test('onClick works as expected when the primary button is clicked', () => { const mockClickHandler = jest.fn(); - const primaryButton = ; + const primaryButton = ( + + ); const children = { primaryButton }; const component = mountWithIntl(); component.find('button').simulate('click'); diff --git a/packages/shared-ux/button_toolbar/src/toolbar/toolbar.tsx b/packages/shared-ux/button_toolbar/src/toolbar/toolbar.tsx index a195ca1b4c3be..eef0ce05eed6e 100644 --- a/packages/shared-ux/button_toolbar/src/toolbar/toolbar.tsx +++ b/packages/shared-ux/button_toolbar/src/toolbar/toolbar.tsx @@ -8,19 +8,19 @@ import React, { ReactElement } from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; - import { i18n } from '@kbn/i18n'; -import { IconButtonGroup, PrimaryButton } from '../buttons'; + import { ToolbarPopover } from '../popover'; +import { IconButtonGroup, ToolbarButton } from '../buttons'; /** type for cases with both button or a popover could be used */ -export type ToolbarButton = typeof PrimaryButton | typeof ToolbarPopover; +export type ToolbarButtonType = typeof ToolbarButton | typeof ToolbarPopover; /** Specific type for the toolbar children in its props */ interface NamedSlots { - primaryButton: ReactElement; + primaryButton: ReactElement; iconButtonGroup?: ReactElement; - extraButtons?: Array> | undefined; + extraButtons?: Array> | undefined; } /** diff --git a/packages/shared-ux/file/file_picker/impl/src/file_picker.stories.tsx b/packages/shared-ux/file/file_picker/impl/src/file_picker.stories.tsx index c314bfb1db65e..37e9a7758ab39 100644 --- a/packages/shared-ux/file/file_picker/impl/src/file_picker.stories.tsx +++ b/packages/shared-ux/file/file_picker/impl/src/file_picker.stories.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { ComponentMeta, ComponentStory } from '@storybook/react'; import { action } from '@storybook/addon-actions'; import { base64dLogo } from '@kbn/shared-ux-file-image-mocks'; -import type { FileImageMetadata, FileKind } from '@kbn/shared-ux-file-types'; +import type { FileImageMetadata, FileKindBrowser } from '@kbn/shared-ux-file-types'; import type { FileJSON, BaseFilesClient as FilesClient } from '@kbn/shared-ux-file-types'; import { FilesContext } from '@kbn/shared-ux-file-context'; import { FilePicker, Props as FilePickerProps } from './file_picker'; @@ -23,7 +23,7 @@ const getFileKind = (id: string) => id: kind, http: {}, allowedMimeTypes: ['*'], - } as FileKind); + } as FileKindBrowser); const defaultProps: FilePickerProps = { kind, diff --git a/packages/shared-ux/file/file_upload/impl/src/file_upload.stories.tsx b/packages/shared-ux/file/file_upload/impl/src/file_upload.stories.tsx index 652d4f618f4dc..8c01e47952b10 100644 --- a/packages/shared-ux/file/file_upload/impl/src/file_upload.stories.tsx +++ b/packages/shared-ux/file/file_upload/impl/src/file_upload.stories.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { ComponentMeta, ComponentStory } from '@storybook/react'; import { action } from '@storybook/addon-actions'; -import { FileKind, BaseFilesClient as FilesClient } from '@kbn/shared-ux-file-types'; +import { FileKindBrowser, BaseFilesClient as FilesClient } from '@kbn/shared-ux-file-types'; import { FilesContext } from '@kbn/shared-ux-file-context'; import { FileUpload, Props } from './file_upload'; @@ -37,7 +37,7 @@ const fileKinds = { allowedMimeTypes: ['application/zip'], }, }; -const getFileKind = (id: string) => (fileKinds as any)[id] as FileKind; +const getFileKind = (id: string) => (fileKinds as any)[id] as FileKindBrowser; const defaultArgs: Props = { kind, diff --git a/packages/shared-ux/file/file_upload/impl/src/upload_state.test.ts b/packages/shared-ux/file/file_upload/impl/src/upload_state.test.ts index 8fe9622fffc6e..644e826a7043d 100644 --- a/packages/shared-ux/file/file_upload/impl/src/upload_state.test.ts +++ b/packages/shared-ux/file/file_upload/impl/src/upload_state.test.ts @@ -9,7 +9,11 @@ import { DeeplyMockedKeys } from '@kbn/utility-types-jest'; import { of, delay, merge, tap, mergeMap } from 'rxjs'; import { TestScheduler } from 'rxjs/testing'; -import type { FileKind, FileJSON, BaseFilesClient as FilesClient } from '@kbn/shared-ux-file-types'; +import type { + FileKindBrowser, + FileJSON, + BaseFilesClient as FilesClient, +} from '@kbn/shared-ux-file-types'; import { createMockFilesClient } from '@kbn/shared-ux-file-mocks'; import { ImageMetadataFactory } from '@kbn/shared-ux-file-util'; @@ -29,7 +33,7 @@ describe('UploadState', () => { filesClient.create.mockReturnValue(of({ file: { id: 'test' } as FileJSON }) as any); filesClient.upload.mockReturnValue(of(undefined) as any); uploadState = new UploadState( - { id: 'test', http: {}, maxSizeBytes: 1000 } as FileKind, + { id: 'test', http: {}, maxSizeBytes: 1000 } as FileKindBrowser, filesClient, {}, imageMetadataFactory @@ -191,7 +195,7 @@ describe('UploadState', () => { it('option "allowRepeatedUploads" calls clear after upload is done', () => { testScheduler.run(({ expectObservable, cold }) => { uploadState = new UploadState( - { id: 'test', http: {}, maxSizeBytes: 1000 } as FileKind, + { id: 'test', http: {}, maxSizeBytes: 1000 } as FileKindBrowser, filesClient, { allowRepeatedUploads: true }, imageMetadataFactory diff --git a/packages/shared-ux/file/file_upload/impl/src/upload_state.ts b/packages/shared-ux/file/file_upload/impl/src/upload_state.ts index fc8a743c3d204..df8b7c79580f9 100644 --- a/packages/shared-ux/file/file_upload/impl/src/upload_state.ts +++ b/packages/shared-ux/file/file_upload/impl/src/upload_state.ts @@ -8,7 +8,11 @@ import * as Rx from 'rxjs'; import { ImageMetadataFactory, getImageMetadata, isImage } from '@kbn/shared-ux-file-util'; -import type { FileKind, FileJSON, BaseFilesClient as FilesClient } from '@kbn/shared-ux-file-types'; +import type { + FileKindBrowser, + FileJSON, + BaseFilesClient as FilesClient, +} from '@kbn/shared-ux-file-types'; import { i18nTexts } from './i18n_texts'; import { createStateSubject, type SimpleStateSubject, parseFileName } from './util'; @@ -48,7 +52,7 @@ export class UploadState { private subscriptions: Rx.Subscription[]; constructor( - private readonly fileKind: FileKind, + private readonly fileKind: FileKindBrowser, private readonly client: FilesClient, private readonly opts: UploadOptions = { allowRepeatedUploads: false }, private readonly loadImageMetadata: ImageMetadataFactory = getImageMetadata @@ -240,7 +244,7 @@ export const createUploadState = ({ imageMetadataFactory, ...options }: { - fileKind: FileKind; + fileKind: FileKindBrowser; client: FilesClient; imageMetadataFactory?: ImageMetadataFactory; } & UploadOptions) => { diff --git a/packages/shared-ux/file/types/README.md b/packages/shared-ux/file/types/README.md index 66e069f505928..c4fc97c25eff7 100644 --- a/packages/shared-ux/file/types/README.md +++ b/packages/shared-ux/file/types/README.md @@ -1,5 +1 @@ # @kbn/shared-ux-link-redirect-app-types - -To generate the types for the file client run. See ./build_file_client.ts - -`yarn ts-node ./packages/shared-ux/file/types/build_file_client.ts` diff --git a/packages/shared-ux/file/types/base_file_client.d.ts b/packages/shared-ux/file/types/base_file_client.ts similarity index 94% rename from packages/shared-ux/file/types/base_file_client.d.ts rename to packages/shared-ux/file/types/base_file_client.ts index 87805f63cfc39..4a00f2de00516 100644 --- a/packages/shared-ux/file/types/base_file_client.d.ts +++ b/packages/shared-ux/file/types/base_file_client.ts @@ -6,7 +6,8 @@ * Side Public License, v 1. */ -import type { FileJSON, FileKind } from '.'; +import type { FileShareJSON, FileShareJSONWithToken } from './sharing'; +import type { FileJSON, FileKindBase } from '.'; export interface Pagination { page?: number; @@ -62,7 +63,7 @@ export interface BaseFilesClient { */ getById: (args: { id: string; kind: string } & Abortable) => Promise<{ file: FileJSON }>; /** - * List all file objects, of a given {@link FileKind}. + * List all file objects, of a given {@link FileKindBrowser}. * * @param args - list files args */ @@ -154,5 +155,5 @@ export interface BaseFilesClient { * Get a file kind * @param id The id of the file kind */ - getFileKind: (id: string) => FileKind; + getFileKind: (id: string) => FileKindBase; } diff --git a/packages/shared-ux/file/types/index.d.ts b/packages/shared-ux/file/types/index.ts similarity index 77% rename from packages/shared-ux/file/types/index.d.ts rename to packages/shared-ux/file/types/index.ts index 9d63635179ca5..4c49124f7149f 100644 --- a/packages/shared-ux/file/types/index.d.ts +++ b/packages/shared-ux/file/types/index.ts @@ -7,6 +7,7 @@ */ export type { BaseFilesClient, Abortable, Pagination } from './base_file_client'; +export type { FileShare, FileShareJSON, FileShareJSONWithToken } from './sharing'; /* Status of a file. * @@ -23,19 +24,6 @@ export type FileStatus = 'AWAITING_UPLOAD' | 'UPLOADING' | 'READY' | 'UPLOAD_ERR */ export type FileCompression = 'br' | 'gzip' | 'deflate' | 'none'; -/** Definition for an endpoint that the File's service will generate */ -interface HttpEndpointDefinition { - /** - * Specify the tags for this endpoint. - * - * @example - * // This will enable access control to this endpoint for users that can access "myApp" only. - * { tags: ['access:myApp'] } - * - */ - tags: string[]; -} - /** * File metadata fields are defined per the ECS specification: * @@ -240,23 +228,11 @@ export interface FileJSON { user?: FileMetadata['user']; } -/* - * A descriptor of meta values associated with a set or "kind" of files. - * - * @note In order to use the file service consumers must register a {@link FileKind} - * in the {@link FileKindsRegistry}. - */ -export interface FileKind { +export interface FileKindBase { /** * Unique file kind ID */ id: string; - /** - * Maximum size, in bytes, a file of this kind can be. - * - * @default 4MiB - */ - maxSizeBytes?: number; /** * The MIME type of the file content. @@ -264,52 +240,16 @@ export interface FileKind { * @default accept all mime types */ allowedMimeTypes?: string[]; +} +export interface FileKindBrowser extends FileKindBase { /** - * Blob store specific settings that enable configuration of storage - * details. - */ - blobStoreSettings?: BlobStorageSettings; - - /** - * Specify which HTTP routes to create for the file kind. + * Max file contents size, in bytes, enforced for this file kind in the upload + * component. * - * You can always create your own HTTP routes for working with files but - * this interface allows you to expose basic CRUD operations, upload, download - * and sharing of files over a RESTful-like interface. - * - * @note The public {@link FileClient} uses these endpoints. + * @default 4MiB */ - http: { - /** - * Expose file creation (and upload) over HTTP. - */ - create?: HttpEndpointDefinition; - /** - * Expose file updates over HTTP. - */ - update?: HttpEndpointDefinition; - /** - * Expose file deletion over HTTP. - */ - delete?: HttpEndpointDefinition; - /** - * Expose "get by ID" functionality over HTTP. - */ - getById?: HttpEndpointDefinition; - /** - * Expose the ability to list all files of this kind over HTTP. - */ - list?: HttpEndpointDefinition; - /** - * Expose the ability to download a file's contents over HTTP. - */ - download?: HttpEndpointDefinition; - /** - * Expose file share functionality over HTTP. - */ - share?: HttpEndpointDefinition; - }; + maxSizeBytes?: number; } /** diff --git a/packages/shared-ux/file/types/sharing.ts b/packages/shared-ux/file/types/sharing.ts new file mode 100644 index 0000000000000..083a6e9b111f5 --- /dev/null +++ b/packages/shared-ux/file/types/sharing.ts @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/** + * Data stored with a file share object + */ +// eslint-disable-next-line @typescript-eslint/consistent-type-definitions +export type FileShare = { + /** + * ISO timestamp of when the file share was created. + */ + created: string; + + /** + * Secret token used to access the associated file. + */ + token: string; + + /** + * Human friendly name for this share token. + */ + name?: string; + + /** + * The unix timestamp (in milliseconds) this file share will expire. + * + * TODO: in future we could add a special value like "forever", but this should + * not be the default. + */ + valid_until: number; +}; + +/** + * Attributes of a file that represent a serialised version of the file. + */ +export interface FileShareJSON { + /** + * Unique ID share instance + */ + id: string; + /** + * ISO timestamp the share was created + */ + created: FileShare['created']; + /** + * Unix timestamp (in milliseconds) of when this share expires + */ + validUntil: FileShare['valid_until']; + /** + * A user-friendly name for the file share + */ + name?: FileShare['name']; + /** + * The ID of the file this share is linked to + */ + fileId: string; +} + +/** + * A version of the file share with a token included. + * + * @note This should only be shown when the file share is first created + */ +export type FileShareJSONWithToken = FileShareJSON & { + /** + * Secret token that can be used to access files + */ + token: string; +}; diff --git a/packages/shared-ux/file/types/tsconfig.json b/packages/shared-ux/file/types/tsconfig.json index 1e6c78a051db3..fef75d832cab6 100644 --- a/packages/shared-ux/file/types/tsconfig.json +++ b/packages/shared-ux/file/types/tsconfig.json @@ -7,7 +7,7 @@ ], }, "include": [ - "*.d.ts" + "*.ts" ], "exclude": [ "target/**/*", diff --git a/src/core/server/integration_tests/saved_objects/migrations/archives/1m_dummy_so.zip b/src/core/server/integration_tests/saved_objects/migrations/archives/1m_dummy_so.zip new file mode 100644 index 0000000000000..18b34d8b1ccb3 Binary files /dev/null and b/src/core/server/integration_tests/saved_objects/migrations/archives/1m_dummy_so.zip differ diff --git a/src/core/server/integration_tests/saved_objects/migrations/group2/batch_size_bytes.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group2/batch_size_bytes.test.ts index f2296d41d905e..17a628d32e1d8 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group2/batch_size_bytes.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group2/batch_size_bytes.test.ts @@ -118,7 +118,7 @@ describe('migration v2', () => { await root.preboot(); await root.setup(); await expect(root.start()).rejects.toMatchInlineSnapshot( - `[Error: Unable to complete saved object migrations for the [.kibana] index: The document with _id "canvas-workpad-template:workpad-template-061d7868-2b4e-4dc8-8bf7-3772b52926e5" is 1715329 bytes which exceeds the configured maximum batch size of 1015275 bytes. To proceed, please increase the 'migrations.maxBatchSizeBytes' Kibana configuration option and ensure that the Elasticsearch 'http.max_content_length' configuration option is set to an equal or larger value.]` + `[Error: Unable to complete saved object migrations for the [.kibana] index: The document with _id "canvas-workpad-template:workpad-template-061d7868-2b4e-4dc8-8bf7-3772b52926e5" is 1715272 bytes which exceeds the configured maximum batch size of 1015275 bytes. To proceed, please increase the 'migrations.maxBatchSizeBytes' Kibana configuration option and ensure that the Elasticsearch 'http.max_content_length' configuration option is set to an equal or larger value.]` ); await retryAsync( @@ -131,7 +131,7 @@ describe('migration v2', () => { expect( records.find((rec) => rec.message.startsWith( - `Unable to complete saved object migrations for the [.kibana] index: The document with _id "canvas-workpad-template:workpad-template-061d7868-2b4e-4dc8-8bf7-3772b52926e5" is 1715329 bytes which exceeds the configured maximum batch size of 1015275 bytes. To proceed, please increase the 'migrations.maxBatchSizeBytes' Kibana configuration option and ensure that the Elasticsearch 'http.max_content_length' configuration option is set to an equal or larger value.` + `Unable to complete saved object migrations for the [.kibana] index: The document with _id "canvas-workpad-template:workpad-template-061d7868-2b4e-4dc8-8bf7-3772b52926e5" is 1715272 bytes which exceeds the configured maximum batch size of 1015275 bytes. To proceed, please increase the 'migrations.maxBatchSizeBytes' Kibana configuration option and ensure that the Elasticsearch 'http.max_content_length' configuration option is set to an equal or larger value.` ) ) ).toBeDefined(); diff --git a/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts index 7d9e4bae782e2..8aa1eb22fccbe 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts @@ -69,12 +69,12 @@ describe('checking migration metadata changes on all registered SO types', () => "canvas-element": "ec334dd45d14291db4d74197e0e42dfe06526868", "canvas-workpad": "ab0525bd5aa4dbad2d6fdb30e6a51bb475254751", "canvas-workpad-template": "c54f2a188a1d0bf18a6cebd9d6f28a7337d41bbf", - "cases": "74c00dfb25f4b109894971bd1090fce4a7c99490", - "cases-comments": "371662a8464e623f1f4f55a981cec78bec4a12f5", - "cases-configure": "25099c9e4bbb91e01e334848c605b4a5de5c9fce", - "cases-connector-mappings": "8de3b77dc6ae8ee62cce2b58a222471dfc3dbdad", + "cases": "1e86563e8364c69f86b77cb6f2933408dd5b827a", + "cases-comments": "69257ec55e8380fdb2ecbddc83e7c26d2ce2a351", + "cases-configure": "66d4c64d83b464f5166005b8ffa03b721fcaaf8b", + "cases-connector-mappings": "877bb4d52e9821e330622bd75fba799490ec6952", "cases-telemetry": "fdeddcef28c75d8c66422475a829e75d37f0b668", - "cases-user-actions": "cfd388d2ca27b3abfd3955dc41428fb229989921", + "cases-user-actions": "8ad74294b71edffa58fad7a40eea2388209477c9", "config": "97e16b8f5dc10c404fd3b201ef36bc6c3c63dc80", "config-global": "d9791e8f73edee884630e1cb6e4954ae513ce75e", "connector_token": "fb05ff5afdcb6e2f20c9c6513ff7a1ab12b66f36", @@ -126,7 +126,7 @@ describe('checking migration metadata changes on all registered SO types', () => "rules-settings": "9854495c3b54b16a6625fb250c35e5504da72266", "sample-data-telemetry": "c38daf1a49ed24f2a4fb091e6e1e833fccf19935", "search": "01bc42d635e9ea0588741c4c7a2bbd3feb3ac5dc", - "search-session": "5f40f6101fc2ec8ce5210d735ea2e00a87c02886", + "search-session": "58a44d14ec991739166b2ec28d718001ab0f4b28", "search-telemetry": "ab67ef721f294f28d5e10febbd20653347720188", "security-rule": "1ff82dfb2298c3caf6888fc3ef15c6bf7a628877", "security-solution-signals-migration": "c2db409c1857d330beb3d6fd188fa186f920302c", @@ -136,7 +136,7 @@ describe('checking migration metadata changes on all registered SO types', () => "siem-ui-timeline-pinned-event": "96a43d59b9e2fc11f12255a0cb47ef0a3d83af4c", "space": "9542afcd6fd71558623c09151e453c5e84b4e5e1", "spaces-usage-stats": "084bd0f080f94fb5735d7f3cf12f13ec92f36bad", - "synthetics-monitor": "5d0a69fac9d6cfdacfa1962274344aecb596167a", + "synthetics-monitor": "96cc312bfa597022f83dfb3b5d1501e27a73e8d5", "synthetics-param": "9776c9b571d35f0d0397e8915e035ea1dc026db7", "synthetics-privates-locations": "7d032fc788905e32152029ae7ab3d6038c48ae44", "tag": "87f21f07df9cc37001b15a26e413c18f50d1fbfe", @@ -145,7 +145,6 @@ describe('checking migration metadata changes on all registered SO types', () => "ui-metric": "410a8ad28e0f44b161c960ff0ce950c712b17c52", "upgrade-assistant-ml-upgrade-operation": "d8816e5ce32649e7a3a43e2c406c632319ff84bb", "upgrade-assistant-reindex-operation": "09ac8ed9c9acf7e8ece8eafe47d7019ea1472144", - "upgrade-assistant-telemetry": "12bcbfc4e4ce64d2ca7c24f9acccd331a2bd2ab6", "uptime-dynamic-settings": "9a63ce80904a04be114749e426882dc3ff011137", "uptime-synthetics-api-key": "599319bedbfa287e8761e1ba49d536417a33fa13", "url": "2422b3cbe0af71f7a9c2e228e19a972e759c56d4", diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/actions/actions.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/actions/actions.test.ts index 7845ec72ab2ec..691800feee0e3 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/actions/actions.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/actions/actions.test.ts @@ -43,6 +43,7 @@ import { type DocumentsTransformFailed, type DocumentsTransformSuccess, MIGRATION_CLIENT_OPTIONS, + createBulkIndexOperationTuple, } from '@kbn/core-saved-objects-migration-server-internal'; const { startES } = createTestServers({ @@ -78,7 +79,7 @@ describe('migration actions', () => { }, }, })(); - const sourceDocs = [ + const docs = [ { _source: { title: 'doc 1' } }, { _source: { title: 'doc 2' } }, { _source: { title: 'doc 3' } }, @@ -88,7 +89,7 @@ describe('migration actions', () => { await bulkOverwriteTransformedDocuments({ client, index: 'existing_index_with_docs', - transformedDocs: sourceDocs, + operations: docs.map(createBulkIndexOperationTuple), refresh: 'wait_for', })(); @@ -101,7 +102,7 @@ describe('migration actions', () => { await bulkOverwriteTransformedDocuments({ client, index: 'existing_index_with_write_block', - transformedDocs: sourceDocs, + operations: docs.map(createBulkIndexOperationTuple), refresh: 'wait_for', })(); await setWriteBlock({ client, index: 'existing_index_with_write_block' })(); @@ -302,7 +303,7 @@ describe('migration actions', () => { const res = (await bulkOverwriteTransformedDocuments({ client, index: 'new_index_without_write_block', - transformedDocs: sourceDocs, + operations: sourceDocs.map(createBulkIndexOperationTuple), refresh: 'wait_for', })()) as Either.Left; @@ -882,7 +883,7 @@ describe('migration actions', () => { await bulkOverwriteTransformedDocuments({ client, index: 'reindex_target_4', - transformedDocs: sourceDocs, + operations: sourceDocs.map(createBulkIndexOperationTuple), refresh: 'wait_for', })(); @@ -1441,7 +1442,7 @@ describe('migration actions', () => { await bulkOverwriteTransformedDocuments({ client, index: 'existing_index_without_mappings', - transformedDocs: sourceDocs, + operations: sourceDocs.map(createBulkIndexOperationTuple), refresh: 'wait_for', })(); @@ -1837,7 +1838,7 @@ describe('migration actions', () => { const task = bulkOverwriteTransformedDocuments({ client, index: 'existing_index_with_docs', - transformedDocs: newDocs, + operations: newDocs.map(createBulkIndexOperationTuple), refresh: 'wait_for', }); @@ -1860,10 +1861,10 @@ describe('migration actions', () => { const task = bulkOverwriteTransformedDocuments({ client, index: 'existing_index_with_docs', - transformedDocs: [ + operations: [ ...existingDocs, { _source: { title: 'doc 8' } } as unknown as SavedObjectsRawDoc, - ], + ].map(createBulkIndexOperationTuple), refresh: 'wait_for', }); await expect(task()).resolves.toMatchInlineSnapshot(` @@ -1883,7 +1884,7 @@ describe('migration actions', () => { bulkOverwriteTransformedDocuments({ client, index: 'existing_index_with_write_block', - transformedDocs: newDocs, + operations: newDocs.map(createBulkIndexOperationTuple), refresh: 'wait_for', })() ).resolves.toMatchInlineSnapshot(` @@ -1906,7 +1907,7 @@ describe('migration actions', () => { const task = bulkOverwriteTransformedDocuments({ client, index: 'existing_index_with_docs', - transformedDocs: newDocs, + operations: newDocs.map(createBulkIndexOperationTuple), }); await expect(task()).resolves.toMatchInlineSnapshot(` Object { diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/active_delete.fixtures.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/active_delete.fixtures.ts new file mode 100644 index 0000000000000..7d605cf116341 --- /dev/null +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/active_delete.fixtures.ts @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { SavedObjectsBulkCreateObject } from '@kbn/core-saved-objects-api-server'; +import type { SavedObjectsType } from '@kbn/core-saved-objects-server'; + +const defaultType: SavedObjectsType = { + name: 'defaultType', + hidden: false, + namespaceType: 'agnostic', + mappings: { + properties: { + name: { type: 'keyword' }, + }, + }, + migrations: {}, +}; + +export const baselineTypes: Array> = [ + { + ...defaultType, + name: 'server', + }, + { + ...defaultType, + name: 'basic', + }, + { + ...defaultType, + name: 'deprecated', + }, + { + ...defaultType, + name: 'complex', + mappings: { + properties: { + name: { type: 'text' }, + value: { type: 'integer' }, + }, + }, + excludeOnUpgrade: () => { + return { + bool: { + must: [{ term: { type: 'complex' } }, { range: { 'complex.value': { lte: 1 } } }], + }, + }; + }, + }, +]; + +export const baselineDocuments: SavedObjectsBulkCreateObject[] = [ + ...['server-foo', 'server-bar', 'server-baz'].map((name) => ({ + type: 'server', + attributes: { + name, + }, + })), + ...['basic-foo', 'basic-bar', 'basic-baz'].map((name) => ({ + type: 'basic', + attributes: { + name, + }, + })), + ...['deprecated-foo', 'deprecated-bar', 'deprecated-baz'].map((name) => ({ + type: 'deprecated', + attributes: { + name, + }, + })), + ...['complex-foo', 'complex-bar', 'complex-baz', 'complex-lipsum'].map((name, index) => ({ + type: 'complex', + attributes: { + name, + value: index, + }, + })), +]; diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/active_delete.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/active_delete.test.ts new file mode 100644 index 0000000000000..80681ffd0a5af --- /dev/null +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/active_delete.test.ts @@ -0,0 +1,335 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import Path from 'path'; +import fs from 'fs/promises'; +import { SemVer } from 'semver'; +import { Env } from '@kbn/config'; +import type { AggregationsAggregate, SearchResponse } from '@elastic/elasticsearch/lib/api/types'; +import { getEnvOptions } from '@kbn/config-mocks'; +import { REPO_ROOT } from '@kbn/repo-info'; +import { createTestServers, type TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server'; +import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; +import type { SavedObjectsType } from '@kbn/core-saved-objects-server'; +import { getKibanaMigratorTestKit } from '../kibana_migrator_test_kit'; +import { baselineDocuments, baselineTypes } from './active_delete.fixtures'; +import { delay } from '../test_utils'; + +const kibanaIndex = '.kibana_migrator_tests'; +export const logFilePath = Path.join(__dirname, 'active_delete.test.log'); +const currentVersion = Env.createDefault(REPO_ROOT, getEnvOptions()).packageInfo.version; +const nextMinor = new SemVer(currentVersion).inc('minor').format(); + +describe('when upgrading to a new stack version', () => { + let esServer: TestElasticsearchUtils['es']; + let esClient: ElasticsearchClient; + + const startElasticsearch = async () => { + const { startES } = createTestServers({ + adjustTimeout: (t: number) => jest.setTimeout(t), + settings: { + es: { + license: 'basic', + }, + }, + }); + return await startES(); + }; + + const createBaseline = async () => { + const { client, runMigrations, savedObjectsRepository } = await getKibanaMigratorTestKit({ + kibanaIndex, + types: baselineTypes, + }); + + await runMigrations(); + + await savedObjectsRepository.bulkCreate(baselineDocuments, { + refresh: 'wait_for', + }); + + return client; + }; + + beforeAll(async () => { + esServer = await startElasticsearch(); + }); + + afterAll(async () => { + await esServer?.stop(); + await delay(10); + }); + + describe('and the mappings match (diffMappings() === false)', () => { + describe('and discardUnknownObjects = true', () => { + let indexContents: SearchResponse<{ type: string }, Record>; + + beforeAll(async () => { + esClient = await createBaseline(); + + await fs.unlink(logFilePath).catch(() => {}); + // remove the 'deprecated' type from the mappings, so that it is considered unknown + const types = baselineTypes.filter((type) => type.name !== 'deprecated'); + const { client, runMigrations } = await getKibanaMigratorTestKit({ + settings: { + migrations: { + discardUnknownObjects: nextMinor, + }, + }, + kibanaIndex, + types, + kibanaVersion: nextMinor, + logFilePath, + }); + + await runMigrations(); + + indexContents = await client.search({ index: kibanaIndex, size: 100 }); + }); + + afterAll(async () => { + await esClient?.indices.delete({ index: `${kibanaIndex}_${currentVersion}_001` }); + }); + + it('the migrator is skipping reindex operation and executing CLEANUP_UNKNOWN_AND_EXCLUDED step', async () => { + const logs = await fs.readFile(logFilePath, 'utf-8'); + expect(logs).toMatch('[.kibana_migrator_tests] INIT -> WAIT_FOR_YELLOW_SOURCE'); + expect(logs).toMatch( + '[.kibana_migrator_tests] WAIT_FOR_YELLOW_SOURCE -> CLEANUP_UNKNOWN_AND_EXCLUDED' + ); + // we gotta inform that we are deleting unknown documents too (discardUnknownObjects: true) + expect(logs).toMatch( + '[.kibana_migrator_tests] Kibana has been configured to discard unknown documents for this migration.' + ); + + expect(logs).toMatch( + 'Therefore, the following documents with unknown types will not be taken into account and they will not be available after the migration:' + ); + expect(logs).toMatch( + '[.kibana_migrator_tests] CLEANUP_UNKNOWN_AND_EXCLUDED -> CLEANUP_UNKNOWN_AND_EXCLUDED_WAIT_FOR_TASK' + ); + expect(logs).toMatch( + '[.kibana_migrator_tests] CLEANUP_UNKNOWN_AND_EXCLUDED_WAIT_FOR_TASK -> PREPARE_COMPATIBLE_MIGRATION' + ); + expect(logs).toMatch( + '[.kibana_migrator_tests] PREPARE_COMPATIBLE_MIGRATION -> REFRESH_TARGET' + ); + expect(logs).toMatch( + '[.kibana_migrator_tests] REFRESH_TARGET -> OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT' + ); + expect(logs).toMatch( + '[.kibana_migrator_tests] CHECK_TARGET_MAPPINGS -> CHECK_VERSION_INDEX_READY_ACTIONS' + ); + expect(logs).toMatch('[.kibana_migrator_tests] CHECK_VERSION_INDEX_READY_ACTIONS -> DONE'); + }); + + describe('CLEANUP_UNKNOWN_AND_EXCLUDED', () => { + it('preserves documents with known types', async () => { + const basicDocumentCount = indexContents.hits.hits.filter( + (result) => result._source?.type === 'basic' + ).length; + + expect(basicDocumentCount).toEqual(3); + }); + + it('deletes documents with unknown types', async () => { + const deprecatedDocumentCount = indexContents.hits.hits.filter( + (result) => result._source?.type === 'deprecated' + ).length; + + expect(deprecatedDocumentCount).toEqual(0); + }); + + it('deletes documents that belong to REMOVED_TYPES', async () => { + const serverDocumentCount = indexContents.hits.hits.filter( + (result) => result._source?.type === 'server' + ).length; + + expect(serverDocumentCount).toEqual(0); + }); + + it("deletes documents that have been excludeOnUpgrade'd via plugin hook", async () => { + const complexDocuments = indexContents.hits.hits.filter( + (result) => result._source?.type === 'complex' + ); + + expect(complexDocuments.length).toEqual(2); + expect(complexDocuments[0]._source).toEqual( + expect.objectContaining({ + complex: { + name: 'complex-baz', + value: 2, + }, + type: 'complex', + }) + ); + expect(complexDocuments[1]._source).toEqual( + expect.objectContaining({ + complex: { + name: 'complex-lipsum', + value: 3, + }, + type: 'complex', + }) + ); + }); + }); + }); + + describe('and discardUnknownObjects = false', () => { + beforeAll(async () => { + esClient = await createBaseline(); + }); + afterAll(async () => { + await esClient?.indices.delete({ index: `${kibanaIndex}_${currentVersion}_001` }); + }); + beforeEach(async () => { + await fs.unlink(logFilePath).catch(() => {}); + }); + + it('fails if unknown documents exist', async () => { + // remove the 'deprecated' type from the mappings, so that SO of this type are considered unknown + const types = baselineTypes.filter((type) => type.name !== 'deprecated'); + const { runMigrations } = await getKibanaMigratorTestKit({ + kibanaIndex, + types, + kibanaVersion: nextMinor, + logFilePath, + }); + + try { + await runMigrations(); + } catch (err) { + const errorMessage = err.message; + expect(errorMessage).toMatch( + 'Unable to complete saved object migrations for the [.kibana_migrator_tests] index: Migration failed because some documents were found which use unknown saved object types:' + ); + expect(errorMessage).toMatch( + 'To proceed with the migration you can configure Kibana to discard unknown saved objects for this migration.' + ); + expect(errorMessage).toMatch(/deprecated:.*\(type: "deprecated"\)/); + } + + const logs = await fs.readFile(logFilePath, 'utf-8'); + expect(logs).toMatch('[.kibana_migrator_tests] INIT -> WAIT_FOR_YELLOW_SOURCE'); + expect(logs).toMatch( + '[.kibana_migrator_tests] WAIT_FOR_YELLOW_SOURCE -> CLEANUP_UNKNOWN_AND_EXCLUDED' + ); + expect(logs).toMatch('[.kibana_migrator_tests] CLEANUP_UNKNOWN_AND_EXCLUDED -> FATAL'); + }); + + it('proceeds if there are no unknown documents', async () => { + const { client, runMigrations } = await getKibanaMigratorTestKit({ + kibanaIndex, + types: baselineTypes, + kibanaVersion: nextMinor, + logFilePath, + }); + + await runMigrations(); + + const logs = await fs.readFile(logFilePath, 'utf-8'); + expect(logs).toMatch('[.kibana_migrator_tests] INIT -> WAIT_FOR_YELLOW_SOURCE'); + expect(logs).toMatch( + '[.kibana_migrator_tests] WAIT_FOR_YELLOW_SOURCE -> CLEANUP_UNKNOWN_AND_EXCLUDED' + ); + expect(logs).toMatch( + '[.kibana_migrator_tests] CLEANUP_UNKNOWN_AND_EXCLUDED -> CLEANUP_UNKNOWN_AND_EXCLUDED_WAIT_FOR_TASK' + ); + expect(logs).toMatch( + '[.kibana_migrator_tests] CLEANUP_UNKNOWN_AND_EXCLUDED_WAIT_FOR_TASK -> PREPARE_COMPATIBLE_MIGRATION' + ); + expect(logs).toMatch( + '[.kibana_migrator_tests] PREPARE_COMPATIBLE_MIGRATION -> REFRESH_TARGET' + ); + expect(logs).toMatch( + '[.kibana_migrator_tests] REFRESH_TARGET -> OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT' + ); + expect(logs).toMatch( + '[.kibana_migrator_tests] CHECK_TARGET_MAPPINGS -> CHECK_VERSION_INDEX_READY_ACTIONS' + ); + expect(logs).toMatch('[.kibana_migrator_tests] CHECK_VERSION_INDEX_READY_ACTIONS -> DONE'); + + const indexContents = await client.search({ index: kibanaIndex, size: 100 }); + + expect(indexContents.hits.hits.length).toEqual(8); + }); + }); + }); + + describe('and the mappings do NOT match (diffMappings() === true)', () => { + beforeAll(async () => { + esClient = await createBaseline(); + }); + afterAll(async () => { + await esClient?.indices.delete({ index: `${kibanaIndex}_${currentVersion}_001` }); + }); + beforeEach(async () => { + await fs.unlink(logFilePath).catch(() => {}); + }); + + it('the migrator does not skip reindexing', async () => { + const incompatibleTypes: Array> = baselineTypes.map((type) => { + if (type.name === 'complex') { + return { + ...type, + mappings: { + properties: { + name: { type: 'keyword' }, // text => keyword + value: { type: 'long' }, // integer => long + }, + }, + }; + } else { + return type; + } + }); + + const { client, runMigrations } = await getKibanaMigratorTestKit({ + kibanaIndex, + types: incompatibleTypes, + kibanaVersion: nextMinor, + logFilePath, + }); + + await runMigrations(); + + const logs = await fs.readFile(logFilePath, 'utf-8'); + expect(logs).toMatch('[.kibana_migrator_tests] INIT -> WAIT_FOR_YELLOW_SOURCE'); + expect(logs).toMatch( + '[.kibana_migrator_tests] WAIT_FOR_YELLOW_SOURCE -> CHECK_UNKNOWN_DOCUMENTS.' + ); + expect(logs).toMatch( + '[.kibana_migrator_tests] CHECK_UNKNOWN_DOCUMENTS -> SET_SOURCE_WRITE_BLOCK.' + ); + expect(logs).toMatch( + '[.kibana_migrator_tests] CHECK_TARGET_MAPPINGS -> UPDATE_TARGET_MAPPINGS.' + ); + expect(logs).toMatch( + '[.kibana_migrator_tests] UPDATE_TARGET_MAPPINGS_META -> CHECK_VERSION_INDEX_READY_ACTIONS.' + ); + expect(logs).toMatch( + '[.kibana_migrator_tests] CHECK_VERSION_INDEX_READY_ACTIONS -> MARK_VERSION_INDEX_READY.' + ); + expect(logs).toMatch('[.kibana_migrator_tests] MARK_VERSION_INDEX_READY -> DONE'); + + const indexContents: SearchResponse< + { type: string }, + Record + > = await client.search({ index: kibanaIndex, size: 100 }); + + expect(indexContents.hits.hits.length).toEqual(8); // we're removing a couple of 'complex' (value < = 1) + + // double-check that the deprecated documents have not been deleted + const deprecatedDocumentCount = indexContents.hits.hits.filter( + (result) => result._source?.type === 'deprecated' + ).length; + expect(deprecatedDocumentCount).toEqual(3); + }); + }); +}); diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/active_delete_multiple_instances.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/active_delete_multiple_instances.test.ts new file mode 100644 index 0000000000000..57e4844ef3182 --- /dev/null +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/active_delete_multiple_instances.test.ts @@ -0,0 +1,186 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import Path from 'path'; +import fs from 'fs/promises'; +import { SemVer } from 'semver'; +import { Env } from '@kbn/config'; +import { getEnvOptions } from '@kbn/config-mocks'; +import { REPO_ROOT } from '@kbn/repo-info'; +import { type TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server'; +import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; +import type { SavedObjectsBulkCreateObject } from '@kbn/core-saved-objects-api-server'; +import { + defaultLogFilePath, + getEsClient, + getKibanaMigratorTestKit, + startElasticsearch, +} from '../kibana_migrator_test_kit'; +import { baselineTypes } from './active_delete.fixtures'; +import { delay } from '../test_utils'; +import { createBaselineArchive } from '../kibana_migrator_archive_utils'; + +const PARALLEL_MIGRATORS = 6; +const DOCUMENTS_PER_TYPE = 250000; + +const kibanaIndex = '.kibana_migrator_tests'; +const currentVersion = Env.createDefault(REPO_ROOT, getEnvOptions()).packageInfo.version; +const nextMinor = new SemVer(currentVersion).inc('minor').format(); + +const dataArchive = Path.join(__dirname, '..', 'archives', '1m_dummy_so.zip'); + +jest.setTimeout(24 * 3600 * 100); + +describe('multiple migrator instances running in parallel', () => { + it.skip('enable and focus this test (it.skip => fit), and run it, in order to create a baseline archive', async () => { + // generate DOCUMENTS_PER_TYPE documents of each type + const documents: SavedObjectsBulkCreateObject[] = ['server', 'basic', 'deprecated', 'complex'] + .map((type) => + new Array(DOCUMENTS_PER_TYPE).fill(true).map((_, index) => ({ + type, + attributes: { + name: `${type}-${++index}`, + ...(type === 'complex' && { value: index }), + }, + })) + ) + .flat(); + + await createBaselineArchive({ kibanaIndex, types: baselineTypes, documents, dataArchive }); + }); + + describe('when upgrading to a new stack version with matching mappings', () => { + let esServer: TestElasticsearchUtils['es']; + let esClient: ElasticsearchClient; + beforeAll(async () => { + esServer = await startElasticsearch({ dataArchive }); + esClient = await getEsClient(); + await fs.unlink(defaultLogFilePath).catch(() => {}); + + for (let i = 0; i < PARALLEL_MIGRATORS; ++i) { + await fs.unlink(Path.join(__dirname, `active_delete_instance_${i}.log`)).catch(() => {}); + } + }); + + it('will actively delete and successfully complete migration', async () => { + const startTime = Date.now(); + const types = baselineTypes + .filter((type) => type.name !== 'deprecated') + .map((type) => { + if (type.name !== 'complex') { + return type; + } + + return { + ...type, + excludeOnUpgrade: () => { + return { + bool: { + must: [ + { term: { type: 'complex' } }, + { range: { 'complex.value': { lte: 125000 } } }, + ], + }, + }; + }, + }; + }); + + const beforeCleanup = await getAggregatedTypesCount(); + expect(beforeCleanup.server).toEqual(DOCUMENTS_PER_TYPE); + expect(beforeCleanup.basic).toEqual(DOCUMENTS_PER_TYPE); + expect(beforeCleanup.deprecated).toEqual(DOCUMENTS_PER_TYPE); + expect(beforeCleanup.complex).toEqual(DOCUMENTS_PER_TYPE); + + const testKits = await Promise.all( + new Array(PARALLEL_MIGRATORS) + .fill({ + settings: { + migrations: { + discardUnknownObjects: nextMinor, + }, + }, + kibanaIndex, + types, + kibanaVersion: nextMinor, + }) + .map((config, index) => + getKibanaMigratorTestKit({ + ...config, + logFilePath: Path.join(__dirname, `active_delete_instance_${index}.log`), + }) + ) + ); + + const results = await Promise.all(testKits.map((testKit) => testKit.runMigrations())); + expect(results.flat().every((result) => result.status === 'migrated')).toEqual(true); + + for (let i = 0; i < PARALLEL_MIGRATORS; ++i) { + const logs = await fs.readFile( + Path.join(__dirname, `active_delete_instance_${i}.log`), + 'utf-8' + ); + expect(logs).toMatch('CHECK_VERSION_INDEX_READY_ACTIONS -> DONE'); + expect(logs).toMatch('Migration completed'); + } + + const endTime = Date.now(); + // eslint-disable-next-line no-console + console.debug(`Migration took: ${(endTime - startTime) / 1000} seconds`); + + // After cleanup + const afterCleanup = await getAggregatedTypesCount(); + expect(afterCleanup.server).not.toBeDefined(); // 'server' is part of the REMOVED_TYPES + expect(afterCleanup.basic).toEqual(DOCUMENTS_PER_TYPE); // we keep 'basic' SOs + expect(afterCleanup.deprecated).not.toBeDefined(); // 'deprecated' is no longer present in nextMinor's mappings + expect(afterCleanup.complex).toEqual(DOCUMENTS_PER_TYPE / 2); // we excludeFromUpgrade half of them with a hook + }); + + afterAll(async () => { + // await esClient?.indices.delete({ index: `${kibanaIndex}_${currentVersion}_001` }); + await esServer?.stop(); + await delay(10); + }); + + const getAggregatedTypesCount = async () => { + await esClient.indices.refresh(); + const response = await esClient.search({ + index: kibanaIndex, + _source: false, + aggs: { + typesAggregation: { + terms: { + // assign type __UNKNOWN__ to those documents that don't define one + missing: '__UNKNOWN__', + field: 'type', + size: 10, + }, + aggs: { + docs: { + top_hits: { + size: 2, + _source: { + excludes: ['*'], + }, + }, + }, + }, + }, + }, + }); + + return (response.aggregations!.typesAggregation.buckets as unknown as any).reduce( + (acc: any, current: any) => { + acc[current.key] = current.doc_count; + return acc; + }, + {} + ); + }; + }); +}); diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/skip_reindex.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/skip_reindex.test.ts index a6060b166335f..aa5d1c0c06eb4 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/skip_reindex.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/skip_reindex.test.ts @@ -64,7 +64,13 @@ describe('skip reindexing', () => { logs = await fs.readFile(logFilePath, 'utf-8'); expect(logs).toMatch('INIT -> WAIT_FOR_YELLOW_SOURCE'); - expect(logs).toMatch('WAIT_FOR_YELLOW_SOURCE -> PREPARE_COMPATIBLE_MIGRATION'); + expect(logs).toMatch('WAIT_FOR_YELLOW_SOURCE -> CLEANUP_UNKNOWN_AND_EXCLUDED'); + expect(logs).toMatch( + 'CLEANUP_UNKNOWN_AND_EXCLUDED -> CLEANUP_UNKNOWN_AND_EXCLUDED_WAIT_FOR_TASK' + ); + expect(logs).toMatch( + 'CLEANUP_UNKNOWN_AND_EXCLUDED_WAIT_FOR_TASK -> PREPARE_COMPATIBLE_MIGRATION' + ); expect(logs).toMatch('PREPARE_COMPATIBLE_MIGRATION -> OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT'); expect(logs).toMatch('CHECK_TARGET_MAPPINGS -> CHECK_VERSION_INDEX_READY_ACTIONS'); expect(logs).toMatch('CHECK_VERSION_INDEX_READY_ACTIONS -> DONE'); @@ -87,7 +93,7 @@ describe('skip reindexing', () => { logs = await fs.readFile(logFilePath, 'utf-8'); expect(logs).toMatch('INIT -> OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT'); - expect(logs).not.toMatch('INIT -> PREPARE_COMPATIBLE_MIGRATION'); + expect(logs).not.toMatch('INIT -> WAIT_FOR_YELLOW_SOURCE'); }); }); diff --git a/src/core/server/integration_tests/saved_objects/migrations/kibana_migrator_archive_utils.ts b/src/core/server/integration_tests/saved_objects/migrations/kibana_migrator_archive_utils.ts new file mode 100644 index 0000000000000..88342c1a66ac3 --- /dev/null +++ b/src/core/server/integration_tests/saved_objects/migrations/kibana_migrator_archive_utils.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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +/* eslint-disable no-console */ + +import Path from 'path'; +import { exec } from 'child_process'; +import { promisify } from 'util'; +const execPromise = promisify(exec); + +import { SavedObjectsBulkCreateObject } from '@kbn/core-saved-objects-api-server'; +import { SavedObjectsType } from '@kbn/core-saved-objects-server'; +import { getKibanaMigratorTestKit, startElasticsearch } from './kibana_migrator_test_kit'; +import { delay } from './test_utils'; + +const DEFAULT_BATCH_SIZE = 100000; + +interface CreateBaselineArchiveParams { + kibanaIndex: string; + types: Array>; + documents: SavedObjectsBulkCreateObject[]; + batchSize?: number; + esBaseFolder?: string; + dataArchive: string; +} + +export const createBaselineArchive = async ({ + types, + documents, + kibanaIndex, + batchSize = DEFAULT_BATCH_SIZE, + esBaseFolder = Path.join(__dirname, `target`), + dataArchive, +}: CreateBaselineArchiveParams) => { + const startTime = Date.now(); + const esServer = await startElasticsearch({ basePath: esBaseFolder }); + + const { runMigrations, savedObjectsRepository } = await getKibanaMigratorTestKit({ + kibanaIndex, + types, + }); + + await runMigrations(); + + const batches = Math.ceil(documents.length / batchSize); + + for (let i = 0; i < batches; ++i) { + console.log(`Indexing up to ${batchSize} docs (batch ${i + 1} of ${batches})`); + await savedObjectsRepository.bulkCreate(documents.slice(batchSize * i, batchSize * (i + 1)), { + refresh: 'wait_for', + }); + } + + await compressBaselineArchive(esBaseFolder, dataArchive); + console.log(`Archive created in: ${(Date.now() - startTime) / 1000} seconds`, dataArchive); + await delay(200); + await esServer.stop(); + // await fs.rm(esBaseFolder, { recursive: true }); +}; + +const compressBaselineArchive = async (esFolder: string, archiveFile: string) => { + const dataFolder = Path.join(esFolder, 'es-test-cluster'); + const cmd = `cd ${dataFolder} && zip -r ${archiveFile} data -x ".DS_Store" -x "__MACOSX"`; + await execPromise(cmd); +}; diff --git a/src/core/server/integration_tests/saved_objects/migrations/kibana_migrator_test_kit.ts b/src/core/server/integration_tests/saved_objects/migrations/kibana_migrator_test_kit.ts new file mode 100644 index 0000000000000..df3cce7dbdca6 --- /dev/null +++ b/src/core/server/integration_tests/saved_objects/migrations/kibana_migrator_test_kit.ts @@ -0,0 +1,274 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import Path from 'path'; +import { defaultsDeep } from 'lodash'; +import { BehaviorSubject, firstValueFrom, map } from 'rxjs'; +import { ConfigService, Env } from '@kbn/config'; +import { getEnvOptions } from '@kbn/config-mocks'; +import { REPO_ROOT } from '@kbn/repo-info'; +import { KibanaMigrator } from '@kbn/core-saved-objects-migration-server-internal'; + +import { + SavedObjectConfig, + type SavedObjectsConfigType, + type SavedObjectsMigrationConfigType, + SavedObjectTypeRegistry, + IKibanaMigrator, + MigrationResult, +} from '@kbn/core-saved-objects-base-server-internal'; +import { SavedObjectsRepository } from '@kbn/core-saved-objects-api-server-internal'; +import { + ElasticsearchConfig, + type ElasticsearchConfigType, +} from '@kbn/core-elasticsearch-server-internal'; +import { AgentManager, configureClient } from '@kbn/core-elasticsearch-client-server-internal'; +import { type LoggingConfigType, LoggingSystem } from '@kbn/core-logging-server-internal'; + +import type { ISavedObjectTypeRegistry, SavedObjectsType } from '@kbn/core-saved-objects-server'; +import { esTestConfig, kibanaServerTestUser } from '@kbn/test'; +import { LoggerFactory } from '@kbn/logging'; +import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; +import { registerServiceConfig } from '@kbn/core-root-server-internal'; +import { ISavedObjectsRepository } from '@kbn/core-saved-objects-api-server'; +import { getDocLinks, getDocLinksMeta } from '@kbn/doc-links'; +import { DocLinksServiceStart } from '@kbn/core-doc-links-server'; +import { createTestServers } from '@kbn/core-test-helpers-kbn-server'; + +export const defaultLogFilePath = Path.join(__dirname, 'kibana_migrator_test_kit.log'); + +const env = Env.createDefault(REPO_ROOT, getEnvOptions()); +// Extract current stack version from Env, to use as a default +const currentVersion = env.packageInfo.version; +const currentBranch = env.packageInfo.branch; + +export interface GetEsClientParams { + settings?: Record; + kibanaVersion?: string; + logFilePath?: string; +} + +export interface KibanaMigratorTestKitParams { + kibanaIndex?: string; + kibanaVersion?: string; + kibanaBranch?: string; + settings?: Record; + types?: Array>; + logFilePath?: string; +} + +export interface KibanaMigratorTestKit { + client: ElasticsearchClient; + migrator: IKibanaMigrator; + runMigrations: (rerun?: boolean) => Promise; + typeRegistry: ISavedObjectTypeRegistry; + savedObjectsRepository: ISavedObjectsRepository; +} + +export const startElasticsearch = async ({ + basePath, + dataArchive, +}: { + basePath?: string; + dataArchive?: string; +}) => { + const { startES } = createTestServers({ + adjustTimeout: (t: number) => jest.setTimeout(t), + settings: { + es: { + license: 'basic', + basePath, + dataArchive, + }, + }, + }); + return await startES(); +}; + +export const getEsClient = async ({ + settings = {}, + kibanaVersion = currentVersion, + logFilePath = defaultLogFilePath, +}: GetEsClientParams = {}) => { + const loggingSystem = new LoggingSystem(); + const loggerFactory = loggingSystem.asLoggerFactory(); + + const configService = getConfigService(settings, loggerFactory, logFilePath); + + // configure logging system + const loggingConf = await firstValueFrom(configService.atPath('logging')); + loggingSystem.upgrade(loggingConf); + + return await getElasticsearchClient(configService, loggerFactory, kibanaVersion); +}; + +export const getKibanaMigratorTestKit = async ({ + settings = {}, + kibanaIndex = '.kibana', + kibanaVersion = currentVersion, + kibanaBranch = currentBranch, + types = [], + logFilePath = defaultLogFilePath, +}: KibanaMigratorTestKitParams = {}): Promise => { + const loggingSystem = new LoggingSystem(); + const loggerFactory = loggingSystem.asLoggerFactory(); + + const configService = getConfigService(settings, loggerFactory, logFilePath); + + // configure logging system + const loggingConf = await firstValueFrom(configService.atPath('logging')); + loggingSystem.upgrade(loggingConf); + + const client = await getElasticsearchClient(configService, loggerFactory, kibanaVersion); + + const typeRegistry = new SavedObjectTypeRegistry(); + + // types must be registered before instantiating the migrator + registerTypes(typeRegistry, types); + + const migrator = await getMigrator( + configService, + client, + typeRegistry, + loggerFactory, + kibanaIndex, + kibanaVersion, + kibanaBranch + ); + + const runMigrations = async (rerun?: boolean) => { + migrator.prepareMigrations(); + return await migrator.runMigrations({ rerun }); + }; + + const savedObjectsRepository = SavedObjectsRepository.createRepository( + migrator, + typeRegistry, + kibanaIndex, + client, + loggerFactory.get('saved_objects') + ); + + return { + client, + migrator, + runMigrations, + typeRegistry, + savedObjectsRepository, + }; +}; + +const getConfigService = ( + settings: Record, + loggerFactory: LoggerFactory, + logFilePath: string +) => { + // Define some basic default kibana settings + const DEFAULTS_SETTINGS = { + server: { + autoListen: true, + // Use the ephemeral port to make sure that tests use the first available + // port and aren't affected by the timing issues in test environment. + port: 0, + xsrf: { disableProtection: true }, + }, + elasticsearch: { + hosts: [esTestConfig.getUrl()], + username: kibanaServerTestUser.username, + password: kibanaServerTestUser.password, + }, + logging: { + appenders: { + file: { + type: 'file', + fileName: logFilePath, + layout: { + type: 'json', + }, + }, + }, + loggers: [ + { + name: 'root', + level: 'info', + appenders: ['file'], + }, + ], + }, + plugins: {}, + migrations: { skip: false }, + }; + + const rawConfigProvider = { + getConfig$: () => new BehaviorSubject(defaultsDeep({}, settings, DEFAULTS_SETTINGS)), + }; + + const configService = new ConfigService(rawConfigProvider, env, loggerFactory); + registerServiceConfig(configService); + return configService; +}; + +const getElasticsearchClient = async ( + configService: ConfigService, + loggerFactory: LoggerFactory, + kibanaVersion: string +) => { + const esClientConfig = await firstValueFrom( + configService + .atPath('elasticsearch') + .pipe(map((rawConfig) => new ElasticsearchConfig(rawConfig))) + ); + + return configureClient(esClientConfig, { + logger: loggerFactory.get('elasticsearch'), + type: 'data', + agentFactoryProvider: new AgentManager(), + kibanaVersion, + }); +}; + +const getMigrator = async ( + configService: ConfigService, + client: ElasticsearchClient, + typeRegistry: ISavedObjectTypeRegistry, + loggerFactory: LoggerFactory, + kibanaIndex: string, + kibanaVersion: string, + kibanaBranch: string +) => { + const savedObjectsConf = await firstValueFrom( + configService.atPath('savedObjects') + ); + const savedObjectsMigrationConf = await firstValueFrom( + configService.atPath('migrations') + ); + const soConfig = new SavedObjectConfig(savedObjectsConf, savedObjectsMigrationConf); + + const docLinks: DocLinksServiceStart = { + ...getDocLinksMeta({ kibanaBranch }), + links: getDocLinks({ kibanaBranch }), + }; + + return new KibanaMigrator({ + client, + typeRegistry, + kibanaIndex, + soMigrationsConfig: soConfig.migration, + kibanaVersion, + logger: loggerFactory.get('savedobjects-service'), + docLinks, + waitForMigrationCompletion: false, // ensure we have an active role in the migration + }); +}; + +const registerTypes = ( + typeRegistry: SavedObjectTypeRegistry, + types?: Array> +) => { + (types || []).forEach((type) => typeRegistry.registerType(type)); +}; diff --git a/src/core/tsconfig.json b/src/core/tsconfig.json index 1791e14334243..d1c69ad2d1e49 100644 --- a/src/core/tsconfig.json +++ b/src/core/tsconfig.json @@ -148,6 +148,7 @@ "@kbn/core-lifecycle-browser", "@kbn/core-custom-branding-browser", "@kbn/core-custom-branding-server", + "@kbn/core-elasticsearch-client-server-internal", ], "exclude": [ "target/**/*", diff --git a/src/dev/eslint/security_eslint_rule_tests.ts b/src/dev/eslint/security_eslint_rule_tests.ts new file mode 100644 index 0000000000000..eac946ebe4b25 --- /dev/null +++ b/src/dev/eslint/security_eslint_rule_tests.ts @@ -0,0 +1,73 @@ +/* + * Copyright 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 * as a from 'lodash'; // eslint-disable-line no-restricted-imports +import * as b from 'lodash/fp'; // eslint-disable-line no-restricted-imports + +import { set as c } from 'lodash'; // eslint-disable-line no-restricted-imports +import { setWith as d } from 'lodash'; // eslint-disable-line no-restricted-imports +import { template as e } from 'lodash'; // eslint-disable-line no-restricted-imports + +// The following import statements can't be tested because they are not in our package.json +// import 'lodash.set'; // eslint-disable-line no-restricted-imports +// import 'lodash.setWith'; // eslint-disable-line no-restricted-imports +// import 'lodash.template'; // eslint-disable-line no-restricted-imports + +import 'lodash/set'; // eslint-disable-line no-restricted-imports +import 'lodash/setWith'; // eslint-disable-line no-restricted-imports +import 'lodash/template'; // eslint-disable-line no-restricted-imports + +import { set as f } from 'lodash/fp'; // eslint-disable-line no-restricted-imports +import { setWith as g } from 'lodash/fp'; // eslint-disable-line no-restricted-imports +import { assoc as h } from 'lodash/fp'; // eslint-disable-line no-restricted-imports +import { assocPath as i } from 'lodash/fp'; // eslint-disable-line no-restricted-imports +import { template as j } from 'lodash/fp'; // eslint-disable-line no-restricted-imports + +import 'lodash/fp/set'; // eslint-disable-line no-restricted-imports +import 'lodash/fp/setWith'; // eslint-disable-line no-restricted-imports +import 'lodash/fp/assoc'; // eslint-disable-line no-restricted-imports +import 'lodash/fp/assocPath'; // eslint-disable-line no-restricted-imports +import 'lodash/fp/template'; // eslint-disable-line no-restricted-imports + +// The following require statements can't be tested because they are not in our package.json +// require('lodash.set'); // eslint-disable-line no-restricted-modules +// require('lodash.setWith'); // eslint-disable-line no-restricted-modules +// require('lodash.template'); // eslint-disable-line no-restricted-modules + +require('lodash/set'); // eslint-disable-line no-restricted-modules +require('lodash/setWith'); // eslint-disable-line no-restricted-modules +require('lodash/template'); // eslint-disable-line no-restricted-modules + +require('lodash/fp/set'); // eslint-disable-line no-restricted-modules +require('lodash/fp/setWith'); // eslint-disable-line no-restricted-modules +require('lodash/fp/assoc'); // eslint-disable-line no-restricted-modules +require('lodash/fp/assocPath'); // eslint-disable-line no-restricted-modules +require('lodash/fp/template'); // eslint-disable-line no-restricted-modules + +const lodash = { + set() {}, + setWith() {}, + assoc() {}, + assocPath() {}, + template() {}, +}; +lodash.set(); // eslint-disable-line no-restricted-properties +lodash.setWith(); // eslint-disable-line no-restricted-properties +lodash.assoc(); // eslint-disable-line no-restricted-properties +lodash.assocPath(); // eslint-disable-line no-restricted-properties +lodash.template(); // eslint-disable-line no-restricted-properties + +const _ = lodash; +_.set(); // eslint-disable-line no-restricted-properties +_.setWith(); // eslint-disable-line no-restricted-properties +_.assoc(); // eslint-disable-line no-restricted-properties +_.assocPath(); // eslint-disable-line no-restricted-properties +_.template(); // eslint-disable-line no-restricted-properties + +// hack to ensure all imported variables are used +module.exports = [a, b, c, d, e, f, g, h, i, j]; diff --git a/src/dev/license_checker/config.ts b/src/dev/license_checker/config.ts index dd0600b71d2b2..e45c89bf69b7a 100644 --- a/src/dev/license_checker/config.ts +++ b/src/dev/license_checker/config.ts @@ -84,6 +84,6 @@ 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.4.0': ['Elastic License 2.0'], - '@elastic/eui@75.1.0': ['SSPL-1.0 OR Elastic License 2.0'], + '@elastic/eui@75.1.2': ['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 }; diff --git a/src/dev/performance/run_performance_cli.ts b/src/dev/performance/run_performance_cli.ts index daec946f8962b..3539ada07e405 100644 --- a/src/dev/performance/run_performance_cli.ts +++ b/src/dev/performance/run_performance_cli.ts @@ -103,7 +103,7 @@ run( process.stdout.write(`--- Starting ES\n`); await procRunner.run('es', { cmd: 'node', - args: ['scripts/es', 'snapshot'], + args: ['scripts/es', 'snapshot', '--license=trial'], cwd: REPO_ROOT, wait: /kbn\/es setup complete/, }); diff --git a/src/dev/storybook/aliases.ts b/src/dev/storybook/aliases.ts index fc80a35a2def8..47ebefcc9c8c7 100644 --- a/src/dev/storybook/aliases.ts +++ b/src/dev/storybook/aliases.ts @@ -18,7 +18,7 @@ export const storybookAliases = { language_documentation_popover: 'packages/kbn-language-documentation-popover/.storybook', chart_icons: 'packages/kbn-chart-icons/.storybook', content_management: 'packages/content-management/.storybook', - content_management_plugin: 'src/plugins/content_management/.storybook', + content_management_examples: 'examples/content_management_examples/.storybook', controls: 'src/plugins/controls/storybook', custom_integrations: 'src/plugins/custom_integrations/storybook', dashboard_enhanced: 'x-pack/plugins/dashboard_enhanced/.storybook', diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap b/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap index c91e491887a99..1a566571b4d6c 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap @@ -1267,7 +1267,7 @@ exports[`PartitionVisComponent should render correct structure for multi-metric "fillColor": [Function], }, "showAccessor": [Function], - "sortPredicate": undefined, + "sortPredicate": [Function], }, ] } diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_color.test.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_color.test.ts index 225f405bac2ca..06fa1d9aae83b 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_color.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_color.test.ts @@ -10,6 +10,15 @@ import type { PaletteOutput, PaletteDefinition } from '@kbn/coloring'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { Datatable } from '@kbn/expressions-plugin/common'; import { byDataColorPaletteMap } from './get_color'; +import { ShapeTreeNode } from '@elastic/charts'; +import type { SeriesLayer } from '@kbn/coloring'; +import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; +import { fieldFormatsMock } from '@kbn/field-formats-plugin/common/mocks'; +import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import { getColor } from './get_color'; +import { createMockVisData, createMockBucketColumns, createMockPieParams } from '../../mocks'; +import { generateFormatters } from '../formatters'; +import { ChartTypes } from '../../../common/types'; describe('#byDataColorPaletteMap', () => { let datatable: Datatable; @@ -88,3 +97,284 @@ describe('#byDataColorPaletteMap', () => { ); }); }); + +describe('getColor', () => { + const visData = createMockVisData(); + const buckets = createMockBucketColumns(); + const visParams = createMockPieParams(); + const colors = ['color1', 'color2', 'color3', 'color4']; + const dataMock = dataPluginMock.createStartContract(); + interface RangeProps { + gte: number; + lt: number; + } + const defaultFormatter = jest.fn((...args) => fieldFormatsMock.deserialize(...args)); + const formatters = generateFormatters(visData, defaultFormatter); + + dataMock.fieldFormats = { + deserialize: jest.fn(() => ({ + convert: jest.fn((s: RangeProps) => { + return `≥ ${s.gte} and < ${s.lt}`; + }), + })), + } as unknown as DataPublicPluginStart['fieldFormats']; + + const getPaletteRegistry = () => { + const mockPalette1: jest.Mocked = { + id: 'default', + title: 'My Palette', + getCategoricalColor: jest.fn((layer: SeriesLayer[]) => colors[layer[0].rankAtDepth]), + getCategoricalColors: jest.fn((num: number) => colors), + toExpression: jest.fn(() => ({ + type: 'expression', + chain: [ + { + type: 'function', + function: 'system_palette', + arguments: { + name: ['default'], + }, + }, + ], + })), + }; + + return { + get: () => mockPalette1, + getAll: () => [mockPalette1], + }; + }; + it('should return the correct color based on the parent sortIndex', () => { + const d = { + dataName: 'ES-Air', + depth: 1, + sortIndex: 0, + parent: { + children: [['ES-Air'], ['Kibana Airlines']], + depth: 0, + sortIndex: 0, + }, + } as unknown as ShapeTreeNode; + const color = getColor( + ChartTypes.PIE, + d, + 0, + false, + {}, + buckets, + visData.rows, + visParams, + getPaletteRegistry(), + { getColor: () => undefined }, + false, + false, + dataMock.fieldFormats, + visData.columns[0], + formatters + ); + expect(color).toEqual(colors[0]); + }); + + it('slices with the same label should have the same color for small multiples', () => { + const d = { + dataName: 'ES-Air', + depth: 1, + sortIndex: 0, + parent: { + children: [['ES-Air'], ['Kibana Airlines']], + depth: 0, + sortIndex: 0, + }, + } as unknown as ShapeTreeNode; + const color = getColor( + ChartTypes.PIE, + d, + 0, + true, + {}, + buckets, + visData.rows, + visParams, + getPaletteRegistry(), + { getColor: () => undefined }, + false, + false, + dataMock.fieldFormats, + visData.columns[0], + formatters + ); + expect(color).toEqual('color3'); + }); + it('returns the overwriteColor if exists', () => { + const d = { + dataName: 'ES-Air', + depth: 1, + sortIndex: 0, + parent: { + children: [['ES-Air'], ['Kibana Airlines']], + depth: 0, + sortIndex: 0, + }, + } as unknown as ShapeTreeNode; + const color = getColor( + ChartTypes.PIE, + d, + 0, + true, + { 'ES-Air': '#000028' }, + buckets, + visData.rows, + visParams, + getPaletteRegistry(), + { getColor: () => undefined }, + false, + false, + dataMock.fieldFormats, + visData.columns[0], + formatters + ); + expect(color).toEqual('#000028'); + }); + + it('returns the overwriteColor for older visualizations with formatted values', () => { + const d = { + dataName: { + gte: 1000, + lt: 2000, + }, + depth: 1, + sortIndex: 0, + parent: { + children: [ + [ + { + gte: 1000, + lt: 2000, + }, + ], + [ + { + gte: 2000, + lt: 3000, + }, + ], + ], + depth: 0, + sortIndex: 0, + }, + } as unknown as ShapeTreeNode; + const visParamsNew = { + ...visParams, + distinctColors: true, + }; + const column = { + ...visData.columns[0], + format: { + id: 'range', + params: { + id: 'number', + }, + }, + }; + const color = getColor( + ChartTypes.PIE, + d, + 0, + true, + { '≥ 1000 and < 2000': '#3F6833' }, + buckets, + visData.rows, + visParamsNew, + getPaletteRegistry(), + { getColor: () => undefined }, + false, + false, + dataMock.fieldFormats, + column, + formatters + ); + expect(color).toEqual('#3F6833'); + }); + + it('should only pass the second layer for mosaic', () => { + const d = { + dataName: 'Second level 1', + depth: 2, + sortIndex: 0, + parent: { + children: [['Second level 1'], ['Second level 2']], + depth: 1, + sortIndex: 0, + parent: { + children: [['First level']], + depth: 0, + sortIndex: 0, + }, + }, + } as unknown as ShapeTreeNode; + const registry = getPaletteRegistry(); + getColor( + ChartTypes.MOSAIC, + d, + 1, + true, + {}, + buckets, + visData.rows, + visParams, + registry, + undefined, + true, + false, + dataMock.fieldFormats, + visData.columns[0], + formatters + ); + expect(registry.get().getCategoricalColor).toHaveBeenCalledWith( + [expect.objectContaining({ name: 'Second level 1' })], + expect.anything(), + expect.anything() + ); + }); + + it('should only pass the first layer for treemap', () => { + const d = { + dataName: 'Second level 1', + depth: 2, + sortIndex: 0, + parent: { + children: [['Second level 1'], ['Second level 2']], + depth: 1, + sortIndex: 0, + parent: { + children: [['First level']], + depth: 0, + sortIndex: 0, + }, + }, + } as unknown as ShapeTreeNode; + const registry = getPaletteRegistry(); + getColor( + ChartTypes.TREEMAP, + d, + 1, + true, + {}, + buckets, + visData.rows, + visParams, + registry, + undefined, + true, + false, + dataMock.fieldFormats, + visData.columns[0], + formatters + ); + expect(registry.get().getCategoricalColor).toHaveBeenCalledWith( + [expect.objectContaining({ name: 'First level' })], + expect.anything(), + expect.anything() + ); + }); +}); diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.test.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.test.ts index d96ada51ba47a..7300aac06329f 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.test.ts @@ -5,294 +5,110 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { ShapeTreeNode } from '@elastic/charts'; -import type { PaletteDefinition, SeriesLayer } from '@kbn/coloring'; -import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; -import { fieldFormatsMock } from '@kbn/field-formats-plugin/common/mocks'; -import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; -import { getColor } from './get_color'; -import { createMockVisData, createMockBucketColumns, createMockPieParams } from '../../mocks'; -import { generateFormatters } from '../formatters'; -import { ChartTypes } from '../../../common/types'; - -const visData = createMockVisData(); -const buckets = createMockBucketColumns(); -const visParams = createMockPieParams(); -const colors = ['color1', 'color2', 'color3', 'color4']; -const dataMock = dataPluginMock.createStartContract(); -interface RangeProps { - gte: number; - lt: number; -} -const defaultFormatter = jest.fn((...args) => fieldFormatsMock.deserialize(...args)); -const formatters = generateFormatters(visData, defaultFormatter); -dataMock.fieldFormats = { - deserialize: jest.fn(() => ({ - convert: jest.fn((s: RangeProps) => { - return `≥ ${s.gte} and < ${s.lt}`; - }), - })), -} as unknown as DataPublicPluginStart['fieldFormats']; +import { ArrayEntry, ArrayNode } from '@elastic/charts'; +import { fieldFormatsMock } from '@kbn/field-formats-plugin/common/mocks'; +import { BucketColumns, ChartTypes } from '../../../common/types'; +import { createMockPieParams, createMockVisData } from '../../mocks'; +import { getPaletteRegistry } from '../../__mocks__/palettes'; +import { getLayers } from './get_layers'; -export const getPaletteRegistry = () => { - const mockPalette1: jest.Mocked = { - id: 'default', - title: 'My Palette', - getCategoricalColor: jest.fn((layer: SeriesLayer[]) => colors[layer[0].rankAtDepth]), - getCategoricalColors: jest.fn((num: number) => colors), - toExpression: jest.fn(() => ({ - type: 'expression', - chain: [ - { - type: 'function', - function: 'system_palette', - arguments: { - name: ['default'], +describe('getLayers', () => { + it('preserves slice order for multi-metric layer', () => { + const visData = createMockVisData(); + const columns: BucketColumns[] = [ + { + id: 'col-0-0', + name: 'Normal column', + meta: { type: 'murmur3' }, + }, + { + id: 'col-0-0', + name: 'multi-metric column', + meta: { + type: 'number', + sourceParams: { + consolidatedMetricsColumn: true, }, }, - ], - })), - }; - - return { - get: () => mockPalette1, - getAll: () => [mockPalette1], - }; -}; - -describe('computeColor', () => { - it('should return the correct color based on the parent sortIndex', () => { - const d = { - dataName: 'ES-Air', - depth: 1, - sortIndex: 0, - parent: { - children: [['ES-Air'], ['Kibana Airlines']], - depth: 0, - sortIndex: 0, }, - } as unknown as ShapeTreeNode; - const color = getColor( + ]; + const visParams = createMockPieParams(); + const layers = getLayers( ChartTypes.PIE, - d, - 0, - false, - {}, - buckets, - visData.rows, + columns, visParams, - getPaletteRegistry(), - { getColor: () => undefined }, - false, - false, - dataMock.fieldFormats, - visData.columns[0], - formatters - ); - expect(color).toEqual(colors[0]); - }); - - it('slices with the same label should have the same color for small multiples', () => { - const d = { - dataName: 'ES-Air', - depth: 1, - sortIndex: 0, - parent: { - children: [['ES-Air'], ['Kibana Airlines']], - depth: 0, - sortIndex: 0, - }, - } as unknown as ShapeTreeNode; - const color = getColor( - ChartTypes.PIE, - d, - 0, - true, + visData, {}, - buckets, - visData.rows, - visParams, - getPaletteRegistry(), - { getColor: () => undefined }, - false, - false, - dataMock.fieldFormats, - visData.columns[0], - formatters - ); - expect(color).toEqual('color3'); - }); - it('returns the overwriteColor if exists', () => { - const d = { - dataName: 'ES-Air', - depth: 1, - sortIndex: 0, - parent: { - children: [['ES-Air'], ['Kibana Airlines']], - depth: 0, - sortIndex: 0, - }, - } as unknown as ShapeTreeNode; - const color = getColor( - ChartTypes.PIE, - d, - 0, - true, - { 'ES-Air': '#000028' }, - buckets, - visData.rows, - visParams, + [], getPaletteRegistry(), - { getColor: () => undefined }, - false, + {}, + fieldFormatsMock, false, - dataMock.fieldFormats, - visData.columns[0], - formatters + false ); - expect(color).toEqual('#000028'); - }); - it('returns the overwriteColor for older visualizations with formatted values', () => { - const d = { - dataName: { - gte: 1000, - lt: 2000, - }, - depth: 1, - sortIndex: 0, - parent: { - children: [ - [ - { - gte: 1000, - lt: 2000, - }, - ], - [ - { - gte: 2000, - lt: 3000, - }, - ], - ], - depth: 0, - sortIndex: 0, - }, - } as unknown as ShapeTreeNode; - const visParamsNew = { - ...visParams, - distinctColors: true, - }; - const column = { - ...visData.columns[0], - format: { - id: 'range', - params: { - id: 'number', - }, - }, - }; - const color = getColor( - ChartTypes.PIE, - d, - 0, - true, - { '≥ 1000 and < 2000': '#3F6833' }, - buckets, - visData.rows, - visParamsNew, - getPaletteRegistry(), - { getColor: () => undefined }, - false, - false, - dataMock.fieldFormats, - column, - formatters - ); - expect(color).toEqual('#3F6833'); - }); + expect(layers[0].sortPredicate).toBeUndefined(); + expect(layers[1].sortPredicate).toBeDefined(); - it('should only pass the second layer for mosaic', () => { - const d = { - dataName: 'Second level 1', - depth: 2, - sortIndex: 0, - parent: { - children: [['Second level 1'], ['Second level 2']], - depth: 1, - sortIndex: 0, - parent: { - children: [['First level']], - depth: 0, - sortIndex: 0, - }, - }, - } as unknown as ShapeTreeNode; - const registry = getPaletteRegistry(); - getColor( - ChartTypes.MOSAIC, - d, - 1, - true, - {}, - buckets, - visData.rows, - visParams, - registry, - undefined, - true, - false, - dataMock.fieldFormats, - visData.columns[0], - formatters - ); - expect(registry.get().getCategoricalColor).toHaveBeenCalledWith( - [expect.objectContaining({ name: 'Second level 1' })], - expect.anything(), - expect.anything() - ); - }); + const testNodes: ArrayEntry[] = [ + [ + '', + { + value: 2, + inputIndex: [3], + } as ArrayNode, + ], + [ + '', + { + value: 1, + inputIndex: [2], + } as ArrayNode, + ], - it('should only pass the first layer for treemap', () => { - const d = { - dataName: 'Second level 1', - depth: 2, - sortIndex: 0, - parent: { - children: [['Second level 1'], ['Second level 2']], - depth: 1, - sortIndex: 0, - parent: { - children: [['First level']], - depth: 0, - sortIndex: 0, - }, - }, - } as unknown as ShapeTreeNode; - const registry = getPaletteRegistry(); - getColor( - ChartTypes.TREEMAP, - d, - 1, - true, - {}, - buckets, - visData.rows, - visParams, - registry, - undefined, - true, - false, - dataMock.fieldFormats, - visData.columns[0], - formatters - ); - expect(registry.get().getCategoricalColor).toHaveBeenCalledWith( - [expect.objectContaining({ name: 'First level' })], - expect.anything(), - expect.anything() - ); + [ + '', + { + value: 3, + inputIndex: [1], + } as ArrayNode, + ], + ]; + + const predicate = layers[1].sortPredicate!; + testNodes.sort(predicate); + + expect(testNodes).toMatchInlineSnapshot(` + Array [ + Array [ + "", + Object { + "inputIndex": Array [ + 1, + ], + "value": 3, + }, + ], + Array [ + "", + Object { + "inputIndex": Array [ + 2, + ], + "value": 1, + }, + ], + Array [ + "", + Object { + "inputIndex": Array [ + 3, + ], + "value": 2, + }, + ], + ] + `); }); }); diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts index 646a0e4b45d2e..93a9cc2b6c7d6 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts @@ -12,7 +12,7 @@ import { FieldFormat } from '@kbn/field-formats-plugin/common'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import type { Datatable, DatatableRow } from '@kbn/expressions-plugin/public'; import { BucketColumns, ChartTypes, PartitionVisParams } from '../../../common/types'; -import { sortPredicateByType } from './sort_predicate'; +import { sortPredicateByType, sortPredicateSaveSourceOrder } from './sort_predicate'; import { byDataColorPaletteMap, getColor } from './get_color'; import { getNodeLabel } from './get_node_labels'; @@ -52,7 +52,7 @@ export const getLayers = ( ); } - const sortPredicate = sortPredicateByType(chartType, visParams, visData, columns); + const sortPredicateForType = sortPredicateByType(chartType, visParams, visData, columns); return columns.map((col, layerIndex) => { return { groupByRollup: (d: Datum) => (col.id ? d[col.id] ?? EMPTY_SLICE : col.name), @@ -62,7 +62,9 @@ export const getLayers = ( layerIndex === 0 && chartType === ChartTypes.MOSAIC ? { ...fillLabel, minFontSize: 14, maxFontSize: 14, clipText: true } : fillLabel, - sortPredicate, + sortPredicate: col.meta?.sourceParams?.consolidatedMetricsColumn + ? sortPredicateSaveSourceOrder() + : sortPredicateForType, shape: { fillColor: (d) => getColor( diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/sort_predicate.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/sort_predicate.ts index 0e4564a9bcfe5..b5992c3b600bf 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/sort_predicate.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/sort_predicate.ts @@ -30,7 +30,7 @@ export const extractUniqTermsMap = (dataTable: Datatable, columnId: string) => {} ); -const sortPredicateSaveSourceOrder: SortPredicatePureFn = +export const sortPredicateSaveSourceOrder: SortPredicatePureFn = () => ([, node1], [, node2]) => { const [index1] = node1.inputIndex ?? []; diff --git a/src/plugins/console/server/services/spec_definitions_service.ts b/src/plugins/console/server/services/spec_definitions_service.ts index b5515ed1308fa..b978c3c326159 100644 --- a/src/plugins/console/server/services/spec_definitions_service.ts +++ b/src/plugins/console/server/services/spec_definitions_service.ts @@ -9,6 +9,7 @@ import _, { merge } from 'lodash'; import globby from 'globby'; import { basename, join, resolve } from 'path'; +import normalizePath from 'normalize-path'; import { readFileSync } from 'fs'; import { jsSpecLoaders } from '../lib'; @@ -115,8 +116,9 @@ export class SpecDefinitionsService { } private loadJSONSpecInDir(dirname: string) { - const generatedFiles = globby.sync(join(dirname, 'generated', '*.json')); - const overrideFiles = globby.sync(join(dirname, 'overrides', '*.json')); + // we need to normalize paths otherwise they don't work on windows, see https://github.com/elastic/kibana/issues/151032 + const generatedFiles = globby.sync(normalizePath(join(dirname, 'generated', '*.json'))); + const overrideFiles = globby.sync(normalizePath(join(dirname, 'overrides', '*.json'))); return generatedFiles.reduce((acc, file) => { const overrideFile = overrideFiles.find((f) => basename(f) === basename(file)); diff --git a/src/plugins/content_management/common/constants.ts b/src/plugins/content_management/common/constants.ts index 5c35a41577b82..90298e1729d89 100644 --- a/src/plugins/content_management/common/constants.ts +++ b/src/plugins/content_management/common/constants.ts @@ -8,4 +8,4 @@ export const PLUGIN_ID = 'contentManagement'; -export const API_ENDPOINT = '/api/content_management'; +export const API_ENDPOINT = '/api/content_management/rpc'; diff --git a/src/plugins/content_management/common/index.ts b/src/plugins/content_management/common/index.ts index 0bc6549a1681b..ac944daf3571b 100644 --- a/src/plugins/content_management/common/index.ts +++ b/src/plugins/content_management/common/index.ts @@ -19,4 +19,7 @@ export type { SearchIn, } from './rpc'; -export { procedureNames, schemas as rpcSchemas } from './rpc'; +export { procedureNames } from './rpc/constants'; + +// intentionally not exporting schemas to not include @kbn/schema in the public bundle +// export { schemas as rpcSchemas } from './rpc'; diff --git a/src/plugins/content_management/common/schemas.ts b/src/plugins/content_management/common/schemas.ts new file mode 100644 index 0000000000000..172e0f4b28662 --- /dev/null +++ b/src/plugins/content_management/common/schemas.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +// exporting schemas separately from the index.ts file to not include @kbn/schema in the public bundle +// should be only used server-side or in jest tests +export { schemas as rpcSchemas } from './rpc'; diff --git a/src/plugins/content_management/public/index.ts b/src/plugins/content_management/public/index.ts index 0bf75e9d1087a..2687287a36c2f 100644 --- a/src/plugins/content_management/public/index.ts +++ b/src/plugins/content_management/public/index.ts @@ -7,6 +7,18 @@ */ import { ContentManagementPlugin } from './plugin'; +export type { CrudClient } from './crud_client'; +export { + ContentClientProvider, + ContentClient, + useCreateContentMutation, + useUpdateContentMutation, + useDeleteContentMutation, + useSearchContentQuery, + useGetContentQuery, + useContentClient, + type QueryOptions, +} from './content_client'; export function plugin() { return new ContentManagementPlugin(); diff --git a/src/plugins/content_management/public/plugin.ts b/src/plugins/content_management/public/plugin.ts index d82e2fadfb3e6..b2798e4224384 100644 --- a/src/plugins/content_management/public/plugin.ts +++ b/src/plugins/content_management/public/plugin.ts @@ -13,8 +13,9 @@ import { SetupDependencies, StartDependencies, } from './types'; -import type { ContentClient } from './content_client'; -import type { ContentTypeRegistry } from './registry'; +import { ContentClient } from './content_client'; +import { ContentTypeRegistry } from './registry'; +import { RpcClient } from './rpc_client'; export class ContentManagementPlugin implements @@ -26,20 +27,17 @@ export class ContentManagementPlugin > { public setup(core: CoreSetup, deps: SetupDependencies) { - // don't actually expose the client and the registry until it is used to avoid increasing bundle size return { registry: {} as ContentTypeRegistry, }; } public start(core: CoreStart, deps: StartDependencies) { - // don't actually expose the client and the registry until it is used to avoid increasing bundle size - // const rpcClient = new RpcClient(core.http); - // const contentTypeRegistry = new ContentTypeRegistry(); - // const contentClient = new ContentClient( - // (contentType) => contentTypeRegistry.get(contentType)?.crud() ?? rpcClient - // ); - // return { client: contentClient, registry: contentTypeRegistry }; - return { client: {} as ContentClient, registry: {} as ContentTypeRegistry }; + const rpcClient = new RpcClient(core.http); + const contentTypeRegistry = new ContentTypeRegistry(); + const contentClient = new ContentClient( + (contentType) => contentTypeRegistry.get(contentType)?.crud ?? rpcClient + ); + return { client: contentClient, registry: contentTypeRegistry }; } } diff --git a/src/plugins/content_management/public/rpc_client/rpc_client.test.ts b/src/plugins/content_management/public/rpc_client/rpc_client.test.ts index 3a76b355795ed..5e75b45be86ec 100644 --- a/src/plugins/content_management/public/rpc_client/rpc_client.test.ts +++ b/src/plugins/content_management/public/rpc_client/rpc_client.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { procedureNames, API_ENDPOINT } from '../../common'; +import { API_ENDPOINT, procedureNames } from '../../common'; import { RpcClient } from './rpc_client'; diff --git a/src/plugins/content_management/public/rpc_client/rpc_client.ts b/src/plugins/content_management/public/rpc_client/rpc_client.ts index 2d3e6d1ed1cce..9ec27342d45f2 100644 --- a/src/plugins/content_management/public/rpc_client/rpc_client.ts +++ b/src/plugins/content_management/public/rpc_client/rpc_client.ts @@ -18,36 +18,44 @@ import type { ProcedureName, } from '../../common'; import type { CrudClient } from '../crud_client/crud_client'; +import type { + GetResponse, + BulkGetResponse, + CreateItemResponse, + DeleteItemResponse, + UpdateItemResponse, + SearchResponse, +} from '../../server/core/crud'; export class RpcClient implements CrudClient { constructor(private http: { post: HttpSetup['post'] }) {} public get(input: I): Promise { - return this.sendMessage('get', input); + return this.sendMessage>('get', input).then((r) => r.item); } public bulkGet(input: I): Promise { - return this.sendMessage('bulkGet', input); + return this.sendMessage>('bulkGet', input).then((r) => r.items); } public create(input: I): Promise { - return this.sendMessage('create', input); + return this.sendMessage>('create', input).then((r) => r.result); } public update(input: I): Promise { - return this.sendMessage('update', input); + return this.sendMessage>('update', input).then((r) => r.result); } public delete(input: I): Promise { - return this.sendMessage('delete', input); + return this.sendMessage('delete', input).then((r) => r.result); } public search(input: I): Promise { - return this.sendMessage('search', input); + return this.sendMessage('search', input).then((r) => r.result); } - private sendMessage = async (name: ProcedureName, input: any): Promise => { - const { result } = await this.http.post<{ result: any }>(`${API_ENDPOINT}/${name}`, { + private sendMessage = async (name: ProcedureName, input: any): Promise => { + const { result } = await this.http.post<{ result: O }>(`${API_ENDPOINT}/${name}`, { body: JSON.stringify(input), }); return result; diff --git a/src/plugins/content_management/server/core/crud.ts b/src/plugins/content_management/server/core/crud.ts index cde3af9f2c2a2..1ac35769d0444 100644 --- a/src/plugins/content_management/server/core/crud.ts +++ b/src/plugins/content_management/server/core/crud.ts @@ -10,7 +10,7 @@ import type { ContentStorage, StorageContext } from './types'; export interface GetResponse { contentTypeId: string; - item?: T; + item: T; } export interface BulkGetResponse { @@ -33,6 +33,11 @@ export interface DeleteItemResponse { result: T; } +export interface SearchResponse { + contentTypeId: string; + result: T; +} + export class ContentCrud implements ContentStorage { private storage: ContentStorage; private eventBus: EventBus; @@ -245,7 +250,7 @@ export class ContentCrud implements ContentStorage { ctx: StorageContext, query: Query, options?: Options - ): Promise> { + ): Promise> { this.eventBus.emit({ type: 'searchItemStart', contentTypeId: this.contentTypeId, diff --git a/src/plugins/content_management/server/index.ts b/src/plugins/content_management/server/index.ts index e56a3b845fa93..659b59ed6880c 100644 --- a/src/plugins/content_management/server/index.ts +++ b/src/plugins/content_management/server/index.ts @@ -14,3 +14,4 @@ export function plugin(initializerContext: PluginInitializerContext) { } export type { ContentManagementServerSetup, ContentManagementServerStart } from './types'; +export type { ContentStorage, StorageContext } from './core'; diff --git a/src/plugins/content_management/server/rpc/procedures/bulk_get.ts b/src/plugins/content_management/server/rpc/procedures/bulk_get.ts index ee8c27271abfd..83de5d7fcedf4 100644 --- a/src/plugins/content_management/server/rpc/procedures/bulk_get.ts +++ b/src/plugins/content_management/server/rpc/procedures/bulk_get.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { rpcSchemas } from '../../../common'; +import { rpcSchemas } from '../../../common/schemas'; import type { BulkGetIn } from '../../../common'; import type { StorageContext, ContentCrud } from '../../core'; import type { ProcedureDefinition } from '../rpc_service'; diff --git a/src/plugins/content_management/server/rpc/procedures/create.ts b/src/plugins/content_management/server/rpc/procedures/create.ts index fe2f4e0ab7d16..9ff7a55fe560c 100644 --- a/src/plugins/content_management/server/rpc/procedures/create.ts +++ b/src/plugins/content_management/server/rpc/procedures/create.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { rpcSchemas } from '../../../common'; +import { rpcSchemas } from '../../../common/schemas'; import type { CreateIn } from '../../../common'; import type { StorageContext, ContentCrud } from '../../core'; import type { ProcedureDefinition } from '../rpc_service'; diff --git a/src/plugins/content_management/server/rpc/procedures/delete.ts b/src/plugins/content_management/server/rpc/procedures/delete.ts index c10cc02356beb..6260b54468039 100644 --- a/src/plugins/content_management/server/rpc/procedures/delete.ts +++ b/src/plugins/content_management/server/rpc/procedures/delete.ts @@ -5,7 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { rpcSchemas } from '../../../common'; +import { rpcSchemas } from '../../../common/schemas'; import type { DeleteIn } from '../../../common'; import type { StorageContext, ContentCrud } from '../../core'; import type { ProcedureDefinition } from '../rpc_service'; diff --git a/src/plugins/content_management/server/rpc/procedures/get.ts b/src/plugins/content_management/server/rpc/procedures/get.ts index 73960ad8f3a2f..536263b0e9d15 100644 --- a/src/plugins/content_management/server/rpc/procedures/get.ts +++ b/src/plugins/content_management/server/rpc/procedures/get.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { rpcSchemas } from '../../../common'; +import { rpcSchemas } from '../../../common/schemas'; import type { GetIn } from '../../../common'; import type { ContentCrud, StorageContext } from '../../core'; import { validate } from '../../utils'; diff --git a/src/plugins/content_management/server/rpc/procedures/search.ts b/src/plugins/content_management/server/rpc/procedures/search.ts index 4c9d9a4a9b4c9..c0fa0b2c807dc 100644 --- a/src/plugins/content_management/server/rpc/procedures/search.ts +++ b/src/plugins/content_management/server/rpc/procedures/search.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { rpcSchemas } from '../../../common'; +import { rpcSchemas } from '../../../common/schemas'; import type { SearchIn } from '../../../common'; import type { StorageContext, ContentCrud } from '../../core'; import type { ProcedureDefinition } from '../rpc_service'; diff --git a/src/plugins/content_management/server/rpc/procedures/update.ts b/src/plugins/content_management/server/rpc/procedures/update.ts index 4a9a7951079e5..9ea3b0487d58b 100644 --- a/src/plugins/content_management/server/rpc/procedures/update.ts +++ b/src/plugins/content_management/server/rpc/procedures/update.ts @@ -5,7 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { rpcSchemas } from '../../../common'; +import { rpcSchemas } from '../../../common/schemas'; import type { UpdateIn } from '../../../common'; import type { StorageContext, ContentCrud } from '../../core'; import type { ProcedureDefinition } from '../rpc_service'; diff --git a/src/plugins/content_management/server/types.ts b/src/plugins/content_management/server/types.ts index 0f4cc3138b5e9..3d11f487fbe7d 100644 --- a/src/plugins/content_management/server/types.ts +++ b/src/plugins/content_management/server/types.ts @@ -6,11 +6,13 @@ * Side Public License, v 1. */ +import { CoreApi } from './core'; + // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface SetupDependencies {} // eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface ContentManagementServerSetup {} +export interface ContentManagementServerSetup extends CoreApi {} // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface ContentManagementServerStart {} diff --git a/src/plugins/content_management/tsconfig.json b/src/plugins/content_management/tsconfig.json index dfdb4b0f93f2a..a5316fbfac1c1 100644 --- a/src/plugins/content_management/tsconfig.json +++ b/src/plugins/content_management/tsconfig.json @@ -3,7 +3,7 @@ "compilerOptions": { "outDir": "target/types", }, - "include": ["common/**/*", "public/**/*", "server/**/*", "demo/**/*"], + "include": ["common/**/*", "public/**/*", "server/**/*"], "kbn_references": [ "@kbn/core", "@kbn/config-schema", diff --git a/src/plugins/controls/public/control_group/component/control_group_component.tsx b/src/plugins/controls/public/control_group/component/control_group_component.tsx index 52cfb97cca3df..a92ee31e83053 100644 --- a/src/plugins/controls/public/control_group/component/control_group_component.tsx +++ b/src/plugins/controls/public/control_group/component/control_group_component.tsx @@ -8,7 +8,7 @@ import '../control_group.scss'; -import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; +import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; import React, { useMemo, useState } from 'react'; import classNames from 'classnames'; import { @@ -30,19 +30,15 @@ import { } from '@dnd-kit/core'; import { ViewMode } from '@kbn/embeddable-plugin/public'; -import { useReduxEmbeddableContext } from '@kbn/presentation-util-plugin/public'; - -import { ControlGroupReduxState } from '../types'; -import { controlGroupReducers } from '../state/control_group_reducers'; import { ControlClone, SortableControl } from './control_group_sortable_item'; +import { useControlGroupContainerContext } from '../control_group_renderer'; +import { ControlGroupStrings } from '../control_group_strings'; export const ControlGroup = () => { // Redux embeddable container Context - const reduxContext = useReduxEmbeddableContext< - ControlGroupReduxState, - typeof controlGroupReducers - >(); + const reduxContext = useControlGroupContainerContext(); const { + embeddableInstance: controlGroup, actions: { setControlOrders }, useEmbeddableSelector: select, useEmbeddableDispatch, @@ -53,6 +49,7 @@ export const ControlGroup = () => { const panels = select((state) => state.explicitInput.panels); const viewMode = select((state) => state.explicitInput.viewMode); const controlStyle = select((state) => state.explicitInput.controlStyle); + const showAddButton = select((state) => state.componentState.showAddButton); const isEditable = viewMode === ViewMode.EDIT; @@ -101,7 +98,7 @@ export const ControlGroup = () => { return ( <> - {idsInOrder.length > 0 ? ( + {idsInOrder.length > 0 || showAddButton ? ( { + {showAddButton && ( + + controlGroup.openAddDataControlFlyout()} + /> + + )} ) : ( diff --git a/src/plugins/controls/public/control_group/control_group_renderer.tsx b/src/plugins/controls/public/control_group/control_group_renderer.tsx index 471e2db1e2dac..88b270349c42b 100644 --- a/src/plugins/controls/public/control_group/control_group_renderer.tsx +++ b/src/plugins/controls/public/control_group/control_group_renderer.tsx @@ -11,29 +11,31 @@ import { isEqual } from 'lodash'; import useLifecycles from 'react-use/lib/useLifecycles'; import React, { useEffect, useMemo, useRef, useState } from 'react'; -import { IEmbeddable } from '@kbn/embeddable-plugin/public'; -import { useReduxEmbeddableContext } from '@kbn/presentation-util-plugin/public'; -import type { Filter, TimeRange, Query } from '@kbn/es-query'; import { compareFilters } from '@kbn/es-query'; +import type { Filter, TimeRange, Query } from '@kbn/es-query'; +import { EmbeddableFactory } from '@kbn/embeddable-plugin/public'; +import { useReduxEmbeddableContext } from '@kbn/presentation-util-plugin/public'; -import { pluginServices } from '../services'; -import { getDefaultControlGroupInput } from '../../common'; import { + ControlGroupCreationOptions, ControlGroupInput, ControlGroupOutput, ControlGroupReduxState, CONTROL_GROUP_TYPE, } from './types'; -import { ControlGroupContainer } from './embeddable/control_group_container'; +import { pluginServices } from '../services'; +import { getDefaultControlGroupInput } from '../../common'; import { controlGroupReducers } from './state/control_group_reducers'; import { controlGroupInputBuilder } from './control_group_input_builder'; +import { ControlGroupContainer } from './embeddable/control_group_container'; +import { ControlGroupContainerFactory } from './embeddable/control_group_container_factory'; export interface ControlGroupRendererProps { filters?: Filter[]; - getInitialInput: ( + getCreationOptions: ( initialInput: Partial, builder: typeof controlGroupInputBuilder - ) => Promise>; + ) => Promise; onLoadComplete?: (controlGroup: ControlGroupContainer) => void; timeRange?: TimeRange; query?: Query; @@ -41,7 +43,7 @@ export interface ControlGroupRendererProps { export const ControlGroupRenderer = ({ onLoadComplete, - getInitialInput, + getCreationOptions, filters, timeRange, query, @@ -57,16 +59,26 @@ export const ControlGroupRenderer = ({ () => { const { embeddable } = pluginServices.getServices(); (async () => { - const factory = embeddable.getEmbeddableFactory< + const factory = embeddable.getEmbeddableFactory(CONTROL_GROUP_TYPE) as EmbeddableFactory< ControlGroupInput, ControlGroupOutput, - IEmbeddable - >(CONTROL_GROUP_TYPE); - const newControlGroup = (await factory?.create({ - id, - ...getDefaultControlGroupInput(), - ...(await getInitialInput(getDefaultControlGroupInput(), controlGroupInputBuilder)), - })) as ControlGroupContainer; + ControlGroupContainer + > & { + create: ControlGroupContainerFactory['create']; + }; + const { initialInput, settings } = await getCreationOptions( + getDefaultControlGroupInput(), + controlGroupInputBuilder + ); + const newControlGroup = (await factory?.create( + { + id, + ...getDefaultControlGroupInput(), + ...initialInput, + }, + undefined, + settings + )) as ControlGroupContainer; if (controlGroupRef.current) { newControlGroup.render(controlGroupRef.current); @@ -105,7 +117,11 @@ export const ControlGroupRenderer = ({ }; export const useControlGroupContainerContext = () => - useReduxEmbeddableContext(); + useReduxEmbeddableContext< + ControlGroupReduxState, + typeof controlGroupReducers, + ControlGroupContainer + >(); // required for dynamic import using React.lazy() // eslint-disable-next-line import/no-default-export diff --git a/src/plugins/controls/public/control_group/editor/control_editor.tsx b/src/plugins/controls/public/control_group/editor/control_editor.tsx index 785e0e50bd78a..61f0c574d3f01 100644 --- a/src/plugins/controls/public/control_group/editor/control_editor.tsx +++ b/src/plugins/controls/public/control_group/editor/control_editor.tsx @@ -53,6 +53,7 @@ import { import { CONTROL_WIDTH_OPTIONS } from './editor_constants'; import { pluginServices } from '../../services'; import { getDataControlFieldRegistry } from './data_control_editor_tools'; +import { useControlGroupContainerContext } from '../control_group_renderer'; interface EditControlProps { embeddable?: ControlEmbeddable; isCreate: boolean; @@ -99,12 +100,16 @@ export const ControlEditor = ({ dataViews: { getIdsWithTitle, getDefaultId, get }, controls: { getControlFactory }, } = pluginServices.getServices(); + + const { useEmbeddableSelector: select } = useControlGroupContainerContext(); + const editorConfig = select((state) => state.componentState.editorConfig); + const [state, setState] = useState({ dataViewListItems: [], }); const [defaultTitle, setDefaultTitle] = useState(); - const [currentTitle, setCurrentTitle] = useState(title); + const [currentTitle, setCurrentTitle] = useState(title ?? ''); const [currentWidth, setCurrentWidth] = useState(width); const [currentGrow, setCurrentGrow] = useState(grow); const [controlEditorValid, setControlEditorValid] = useState(false); @@ -170,48 +175,50 @@ export const ControlEditor = ({ - - { - setLastUsedDataViewId?.(dataViewId); - if (dataViewId === dataView?.id) return; + {!editorConfig?.hideDataViewSelector && ( + + { + setLastUsedDataViewId?.(dataViewId); + if (dataViewId === dataView?.id) return; - onTypeEditorChange({ dataViewId }); - setSelectedField(undefined); - get(dataViewId).then((newDataView) => { - setState((s) => ({ ...s, selectedDataView: newDataView })); - }); - }} - trigger={{ - label: - state.selectedDataView?.getName() ?? - ControlGroupStrings.manageControl.getSelectDataViewMessage(), - }} - /> - - - { - return Boolean(fieldRegistry?.[field.name]); - }} - selectedFieldName={selectedField} - dataView={dataView} - onSelectField={(field) => { - onTypeEditorChange({ - fieldName: field.name, - }); - const newDefaultTitle = field.displayName ?? field.name; - setDefaultTitle(newDefaultTitle); - setSelectedField(field.name); - if (!currentTitle || currentTitle === defaultTitle) { - setCurrentTitle(newDefaultTitle); - updateTitle(newDefaultTitle); - } - }} - /> - + onTypeEditorChange({ dataViewId }); + setSelectedField(undefined); + get(dataViewId).then((newDataView) => { + setState((s) => ({ ...s, selectedDataView: newDataView })); + }); + }} + trigger={{ + label: + state.selectedDataView?.getName() ?? + ControlGroupStrings.manageControl.getSelectDataViewMessage(), + }} + /> + + )} + {fieldRegistry && ( + + Boolean(fieldRegistry[field.name])} + selectedFieldName={selectedField} + dataView={dataView} + onSelectField={(field) => { + onTypeEditorChange({ + fieldName: field.name, + }); + const newDefaultTitle = field.displayName ?? field.name; + setDefaultTitle(newDefaultTitle); + setSelectedField(field.name); + if (!currentTitle || currentTitle === defaultTitle) { + setCurrentTitle(newDefaultTitle); + updateTitle(newDefaultTitle); + } + }} + /> + + )} {factory ? ( @@ -239,44 +246,48 @@ export const ControlEditor = ({ }} /> - - <> - { - setCurrentWidth(newWidth as ControlWidth); - updateWidth(newWidth as ControlWidth); - }} - /> - {updateGrow && ( - <> - - { - setCurrentGrow(!currentGrow); - updateGrow(!currentGrow); - }} - data-test-subj="control-editor-grow-switch" - /> - - )} - - - {CustomSettings && (factory as IEditableControlFactory).controlEditorOptionsComponent && ( - - + {!editorConfig?.hideWidthSettings && ( + + <> + { + setCurrentWidth(newWidth as ControlWidth); + updateWidth(newWidth as ControlWidth); + }} + /> + {updateGrow && ( + <> + + { + setCurrentGrow(!currentGrow); + updateGrow(!currentGrow); + }} + data-test-subj="control-editor-grow-switch" + /> + + )} + )} + {!editorConfig?.hideAdditionalSettings && + CustomSettings && + (factory as IEditableControlFactory).controlEditorOptionsComponent && ( + + + + )} {removeControl && ( <> diff --git a/src/plugins/controls/public/control_group/editor/edit_control.tsx b/src/plugins/controls/public/control_group/editor/edit_control.tsx index ac258f8ddc3e8..b0d378f164d78 100644 --- a/src/plugins/controls/public/control_group/editor/edit_control.tsx +++ b/src/plugins/controls/public/control_group/editor/edit_control.tsx @@ -13,19 +13,18 @@ import React, { useEffect, useRef } from 'react'; import { OverlayRef } from '@kbn/core/public'; import { toMountPoint } from '@kbn/kibana-react-plugin/public'; import { EmbeddableFactoryNotFoundError } from '@kbn/embeddable-plugin/public'; -import { useReduxEmbeddableContext } from '@kbn/presentation-util-plugin/public'; -import { ControlGroupReduxState } from '../types'; -import { ControlEditor } from './control_editor'; -import { pluginServices } from '../../services'; -import { ControlGroupStrings } from '../control_group_strings'; + import { - IEditableControlFactory, ControlInput, DataControlInput, ControlEmbeddable, + IEditableControlFactory, } from '../../types'; -import { controlGroupReducers } from '../state/control_group_reducers'; -import { ControlGroupContainer, setFlyoutRef } from '../embeddable/control_group_container'; +import { pluginServices } from '../../services'; +import { ControlEditor } from './control_editor'; +import { ControlGroupStrings } from '../control_group_strings'; +import { setFlyoutRef } from '../embeddable/control_group_container'; +import { useControlGroupContainerContext } from '../control_group_renderer'; interface EditControlResult { type: string; @@ -40,11 +39,7 @@ export const EditControlButton = ({ embeddableId }: { embeddableId: string }) => theme: { theme$ }, } = pluginServices.getServices(); // Redux embeddable container Context - const reduxContext = useReduxEmbeddableContext< - ControlGroupReduxState, - typeof controlGroupReducers, - ControlGroupContainer - >(); + const reduxContext = useControlGroupContainerContext(); const { embeddableInstance: controlGroup, actions: { setControlWidth, setControlGrow }, @@ -126,41 +121,45 @@ export const EditControlButton = ({ embeddableId }: { embeddableId: string }) => ref.close(); }; + const ReduxWrapper = controlGroup.getReduxEmbeddableTools().Wrapper; + const flyoutInstance = openFlyout( toMountPoint( - onCancel(flyoutInstance)} - updateTitle={(newTitle) => (inputToReturn.title = newTitle)} - setLastUsedDataViewId={(lastUsed) => controlGroup.setLastUsedDataViewId(lastUsed)} - updateWidth={(newWidth) => - dispatch(setControlWidth({ width: newWidth, embeddableId })) - } - updateGrow={(grow) => dispatch(setControlGrow({ grow, embeddableId }))} - onTypeEditorChange={(partialInput) => { - inputToReturn = { ...inputToReturn, ...partialInput }; - }} - onSave={(type) => onSave(flyoutInstance, type)} - removeControl={() => { - openConfirm(ControlGroupStrings.management.deleteControls.getSubtitle(), { - confirmButtonText: ControlGroupStrings.management.deleteControls.getConfirm(), - cancelButtonText: ControlGroupStrings.management.deleteControls.getCancel(), - title: ControlGroupStrings.management.deleteControls.getDeleteTitle(), - buttonColor: 'danger', - }).then((confirmed) => { - if (confirmed) { - controlGroup.removeEmbeddable(embeddableId); - removed = true; - flyoutInstance.close(); - } - }); - }} - /> + + onCancel(flyoutInstance)} + updateTitle={(newTitle) => (inputToReturn.title = newTitle)} + setLastUsedDataViewId={(lastUsed) => controlGroup.setLastUsedDataViewId(lastUsed)} + updateWidth={(newWidth) => + dispatch(setControlWidth({ width: newWidth, embeddableId })) + } + updateGrow={(grow) => dispatch(setControlGrow({ grow, embeddableId }))} + onTypeEditorChange={(partialInput) => { + inputToReturn = { ...inputToReturn, ...partialInput }; + }} + onSave={(type) => onSave(flyoutInstance, type)} + removeControl={() => { + openConfirm(ControlGroupStrings.management.deleteControls.getSubtitle(), { + confirmButtonText: ControlGroupStrings.management.deleteControls.getConfirm(), + cancelButtonText: ControlGroupStrings.management.deleteControls.getCancel(), + title: ControlGroupStrings.management.deleteControls.getDeleteTitle(), + buttonColor: 'danger', + }).then((confirmed) => { + if (confirmed) { + controlGroup.removeEmbeddable(embeddableId); + removed = true; + flyoutInstance.close(); + } + }); + }} + /> + , { theme$ } ), diff --git a/src/plugins/controls/public/control_group/editor/open_add_data_control_flyout.tsx b/src/plugins/controls/public/control_group/editor/open_add_data_control_flyout.tsx index 1b417f09e27d0..8f426b2d310d8 100644 --- a/src/plugins/controls/public/control_group/editor/open_add_data_control_flyout.tsx +++ b/src/plugins/controls/public/control_group/editor/open_add_data_control_flyout.tsx @@ -31,6 +31,7 @@ export function openAddDataControlFlyout(this: ControlGroupContainer) { theme: { theme$ }, } = pluginServices.getServices(); const ControlsServicesProvider = pluginServices.getContextProvider(); + const ReduxWrapper = this.getReduxEmbeddableTools().Wrapper; let controlInput: Partial = {}; const onCancel = () => { @@ -54,43 +55,45 @@ export function openAddDataControlFlyout(this: ControlGroupContainer) { const flyoutInstance = openFlyout( toMountPoint( - this.setLastUsedDataViewId(newId)} - getRelevantDataViewId={this.getMostRelevantDataViewId} - isCreate={true} - width={this.getInput().defaultControlWidth ?? DEFAULT_CONTROL_WIDTH} - grow={this.getInput().defaultControlGrow ?? DEFAULT_CONTROL_GROW} - updateTitle={(newTitle) => (controlInput.title = newTitle)} - updateWidth={(defaultControlWidth) => this.updateInput({ defaultControlWidth })} - updateGrow={(defaultControlGrow: boolean) => this.updateInput({ defaultControlGrow })} - onSave={(type) => { - this.closeAllFlyouts(); - if (!type) { - return; - } + + this.setLastUsedDataViewId(newId)} + getRelevantDataViewId={this.getMostRelevantDataViewId} + isCreate={true} + width={this.getInput().defaultControlWidth ?? DEFAULT_CONTROL_WIDTH} + grow={this.getInput().defaultControlGrow ?? DEFAULT_CONTROL_GROW} + updateTitle={(newTitle) => (controlInput.title = newTitle)} + updateWidth={(defaultControlWidth) => this.updateInput({ defaultControlWidth })} + updateGrow={(defaultControlGrow: boolean) => this.updateInput({ defaultControlGrow })} + onSave={(type) => { + this.closeAllFlyouts(); + if (!type) { + return; + } - const factory = getControlFactory(type) as IEditableControlFactory; - if (factory.presaveTransformFunction) { - controlInput = factory.presaveTransformFunction(controlInput); - } + const factory = getControlFactory(type) as IEditableControlFactory; + if (factory.presaveTransformFunction) { + controlInput = factory.presaveTransformFunction(controlInput); + } - if (type === OPTIONS_LIST_CONTROL) { - this.addOptionsListControl(controlInput as AddOptionsListControlProps); - return; - } + if (type === OPTIONS_LIST_CONTROL) { + this.addOptionsListControl(controlInput as AddOptionsListControlProps); + return; + } - if (type === RANGE_SLIDER_CONTROL) { - this.addRangeSliderControl(controlInput as AddRangeSliderControlProps); - return; - } + if (type === RANGE_SLIDER_CONTROL) { + this.addRangeSliderControl(controlInput as AddRangeSliderControlProps); + return; + } - this.addDataControlFromField(controlInput as AddDataControlProps); - }} - onCancel={onCancel} - onTypeEditorChange={(partialInput) => - (controlInput = { ...controlInput, ...partialInput }) - } - /> + this.addDataControlFromField(controlInput as AddDataControlProps); + }} + onCancel={onCancel} + onTypeEditorChange={(partialInput) => + (controlInput = { ...controlInput, ...partialInput }) + } + /> + , { theme$ } ), diff --git a/src/plugins/controls/public/control_group/embeddable/control_group_container.tsx b/src/plugins/controls/public/control_group/embeddable/control_group_container.tsx index 703468dd2da76..35b84656c7dbe 100644 --- a/src/plugins/controls/public/control_group/embeddable/control_group_container.tsx +++ b/src/plugins/controls/public/control_group/embeddable/control_group_container.tsx @@ -21,6 +21,7 @@ import { ControlGroupInput, ControlGroupOutput, ControlGroupReduxState, + ControlGroupSettings, ControlPanelState, ControlsPanels, CONTROL_GROUP_TYPE, @@ -87,7 +88,9 @@ export class ControlGroupContainer extends Container< }; public getMostRelevantDataViewId = () => { - return this.lastUsedDataViewId ?? this.relevantDataViewId; + const staticDataViewId = + this.getReduxEmbeddableTools().getState().componentState.staticDataViewId; + return staticDataViewId ?? this.lastUsedDataViewId ?? this.relevantDataViewId; }; public getReduxEmbeddableTools = () => { @@ -134,7 +137,8 @@ export class ControlGroupContainer extends Container< constructor( reduxEmbeddablePackage: ReduxEmbeddablePackage, initialInput: ControlGroupInput, - parent?: Container + parent?: Container, + settings?: ControlGroupSettings ) { super( initialInput, @@ -155,6 +159,7 @@ export class ControlGroupContainer extends Container< >({ embeddable: this, reducers: controlGroupReducers, + initialComponentState: settings, }); // when all children are ready setup subscriptions diff --git a/src/plugins/controls/public/control_group/embeddable/control_group_container_factory.ts b/src/plugins/controls/public/control_group/embeddable/control_group_container_factory.ts index 366ad399baeea..b4fe9285d3bb1 100644 --- a/src/plugins/controls/public/control_group/embeddable/control_group_container_factory.ts +++ b/src/plugins/controls/public/control_group/embeddable/control_group_container_factory.ts @@ -19,7 +19,7 @@ import { Container, EmbeddableFactoryDefinition } from '@kbn/embeddable-plugin/p import { lazyLoadReduxEmbeddablePackage } from '@kbn/presentation-util-plugin/public'; import { EmbeddablePersistableStateService } from '@kbn/embeddable-plugin/common'; -import { ControlGroupInput, CONTROL_GROUP_TYPE } from '../types'; +import { ControlGroupInput, ControlGroupSettings, CONTROL_GROUP_TYPE } from '../types'; import { createControlGroupExtract, createControlGroupInject, @@ -49,9 +49,13 @@ export class ControlGroupContainerFactory implements EmbeddableFactoryDefinition return getDefaultControlGroupInput(); } - public create = async (initialInput: ControlGroupInput, parent?: Container) => { + public create = async ( + initialInput: ControlGroupInput, + parent?: Container, + settings?: ControlGroupSettings + ) => { const reduxEmbeddablePackage = await lazyLoadReduxEmbeddablePackage(); const { ControlGroupContainer } = await import('./control_group_container'); - return new ControlGroupContainer(reduxEmbeddablePackage, initialInput, parent); + return new ControlGroupContainer(reduxEmbeddablePackage, initialInput, parent, settings); }; } diff --git a/src/plugins/controls/public/control_group/types.ts b/src/plugins/controls/public/control_group/types.ts index 8304c9dd0d095..98455b5aab527 100644 --- a/src/plugins/controls/public/control_group/types.ts +++ b/src/plugins/controls/public/control_group/types.ts @@ -15,7 +15,26 @@ export type ControlGroupOutput = ContainerOutput & Omit & { dataViewIds: string[] }; // public only - redux embeddable state type -export type ControlGroupReduxState = ReduxEmbeddableState; +export type ControlGroupReduxState = ReduxEmbeddableState< + ControlGroupInput, + ControlGroupOutput, + ControlGroupSettings +>; + +export interface ControlGroupCreationOptions { + initialInput?: Partial; + settings?: ControlGroupSettings; +} + +export interface ControlGroupSettings { + showAddButton?: boolean; + staticDataViewId?: string; + editorConfig?: { + hideDataViewSelector?: boolean; + hideWidthSettings?: boolean; + hideAdditionalSettings?: boolean; + }; +} export { type ControlsPanels, diff --git a/src/plugins/dashboard/public/dashboard_app/_dashboard_app_strings.ts b/src/plugins/dashboard/public/dashboard_app/_dashboard_app_strings.ts index f7d710aaa0f95..4144d9cd5b3af 100644 --- a/src/plugins/dashboard/public/dashboard_app/_dashboard_app_strings.ts +++ b/src/plugins/dashboard/public/dashboard_app/_dashboard_app_strings.ts @@ -68,6 +68,11 @@ export const getCreateVisualizationButtonTitle = () => defaultMessage: 'Create visualization', }); +export const getQuickCreateButtonGroupLegend = () => + i18n.translate('dashboard.solutionToolbar.quickCreateButtonGroupLegend', { + defaultMessage: 'Shortcuts to popular visualization types', + }); + export const getNewDashboardTitle = () => i18n.translate('dashboard.savedDashboard.newDashboardTitle', { defaultMessage: 'New Dashboard', diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/controls_toolbar_button/controls_toolbar_button.tsx b/src/plugins/dashboard/public/dashboard_app/top_nav/controls_toolbar_button/controls_toolbar_button.tsx index 837fa7c28f313..116f735fb6aac 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/controls_toolbar_button/controls_toolbar_button.tsx +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/controls_toolbar_button/controls_toolbar_button.tsx @@ -7,20 +7,20 @@ */ import React from 'react'; + import { EuiContextMenuPanel } from '@elastic/eui'; -import { SolutionToolbarPopover } from '@kbn/presentation-util-plugin/public'; +import { ToolbarPopover } from '@kbn/shared-ux-button-toolbar'; import type { ControlGroupContainer } from '@kbn/controls-plugin/public'; + import { getControlButtonTitle } from '../../_dashboard_app_strings'; import { AddDataControlButton } from './add_data_control_button'; import { AddTimeSliderControlButton } from './add_time_slider_control_button'; export function ControlsToolbarButton({ controlGroup }: { controlGroup: ControlGroupContainer }) { return ( - @@ -33,6 +33,6 @@ export function ControlsToolbarButton({ controlGroup }: { controlGroup: ControlG ]} /> )} - + ); } diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_editing_toolbar.tsx b/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_editing_toolbar.tsx index b22eddc8e1dc6..f3b67afbac91d 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_editing_toolbar.tsx +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_editing_toolbar.tsx @@ -8,23 +8,21 @@ import { METRIC_TYPE } from '@kbn/analytics'; import { EmbeddableFactory } from '@kbn/embeddable-plugin/public'; -import { - AddFromLibraryButton, - PrimaryActionButton, - QuickButtonGroup, - QuickButtonProps, - SolutionToolbar, -} from '@kbn/presentation-util-plugin/public'; +import { AddFromLibraryButton, Toolbar, ToolbarButton } from '@kbn/shared-ux-button-toolbar'; +import { IconButton, IconButtonGroup } from '@kbn/shared-ux-button-toolbar'; import { BaseVisType, VisTypeAlias } from '@kbn/visualizations-plugin/public'; import React from 'react'; import { useCallback } from 'react'; -import { useEuiTheme } from '@elastic/eui'; +import { IconType, useEuiTheme } from '@elastic/eui'; import { css } from '@emotion/react'; import { dashboardReplacePanelActionStrings } from '../../dashboard_actions/_dashboard_actions_strings'; import { DASHBOARD_APP_ID, DASHBOARD_UI_METRIC_ID } from '../../dashboard_constants'; import { useDashboardContainerContext } from '../../dashboard_container/dashboard_container_context'; import { pluginServices } from '../../services/plugin_services'; -import { getCreateVisualizationButtonTitle } from '../_dashboard_app_strings'; +import { + getCreateVisualizationButtonTitle, + getQuickCreateButtonGroupLegend, +} from '../_dashboard_app_strings'; import { EditorMenu } from './editor_menu'; import { ControlsToolbarButton } from './controls_toolbar_button'; @@ -33,7 +31,6 @@ export function DashboardEditingToolbar() { usageCollection, data: { search }, notifications: { toasts }, - settings: { uiSettings }, embeddable: { getStateTransfer, getEmbeddableFactory }, visualizations: { get: getVisualization, getAliases: getVisTypeAliases }, } = pluginServices.getServices(); @@ -42,7 +39,6 @@ export function DashboardEditingToolbar() { const { embeddableInstance: dashboardContainer } = useDashboardContainerContext(); const stateTransferService = getStateTransfer(); - const IS_DARK_THEME = uiSettings.get('theme:darkMode'); const lensAlias = getVisTypeAliases().find(({ name }) => name === 'lens'); const quickButtonVisTypes: Array< @@ -120,7 +116,9 @@ export function DashboardEditingToolbar() { [trackUiMetric, dashboardContainer, toasts] ); - const getVisTypeQuickButton = (quickButtonForType: typeof quickButtonVisTypes[0]) => { + const getVisTypeQuickButton = ( + quickButtonForType: typeof quickButtonVisTypes[0] + ): IconButton | undefined => { if (quickButtonForType.type === 'vis') { const visTypeName = quickButtonForType.visType; const visType = @@ -130,19 +128,17 @@ export function DashboardEditingToolbar() { if (visType) { if ('aliasPath' in visType) { const { name, icon, title } = visType as VisTypeAlias; - return { + label: title, iconType: icon, - createType: title, onClick: createNewVisType(visType as VisTypeAlias), 'data-test-subj': `dashboardQuickButton${name}`, }; } else { - const { name, icon, title, titleInWizard } = visType as BaseVisType; - + const { name, icon, title, titleInWizard } = visType as BaseVisType & { icon: IconType }; return { + label: titleInWizard || title, iconType: icon, - createType: titleInWizard || title, onClick: createNewVisType(visType as BaseVisType), 'data-test-subj': `dashboardQuickButton${name}`, }; @@ -151,22 +147,25 @@ export function DashboardEditingToolbar() { } else { const embeddableType = quickButtonForType.embeddableType; const embeddableFactory = getEmbeddableFactory(embeddableType); - return { - iconType: embeddableFactory?.getIconType(), - createType: embeddableFactory?.getDisplayName(), - onClick: () => { - if (embeddableFactory) { - createNewEmbeddable(embeddableFactory); - } - }, - 'data-test-subj': `dashboardQuickButton${embeddableType}`, - }; + if (embeddableFactory) { + return { + label: embeddableFactory.getDisplayName(), + iconType: embeddableFactory.getIconType(), + onClick: () => { + if (embeddableFactory) { + createNewEmbeddable(embeddableFactory); + } + }, + 'data-test-subj': `dashboardQuickButton${embeddableType}`, + }; + } } }; - const quickButtons = quickButtonVisTypes - .map(getVisTypeQuickButton) - .filter((button) => button) as QuickButtonProps[]; + const quickButtons: IconButton[] = quickButtonVisTypes.reduce((accumulator, type) => { + const button = getVisTypeQuickButton(type); + return button ? [...accumulator, button] : accumulator; + }, [] as IconButton[]); const extraButtons = [ , @@ -185,21 +184,23 @@ export function DashboardEditingToolbar() { padding: 0 ${euiTheme.size.s} ${euiTheme.size.s} ${euiTheme.size.s}; `} > - + {{ - primaryActionButton: ( - ), - quickButtonGroup: , + iconButtonGroup: ( + + ), extraButtons, }} - +
    ); } diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.tsx b/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.tsx index 5de32e3b10019..46ab6214e7ffd 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.tsx +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.tsx @@ -16,8 +16,8 @@ import { EuiFlexItem, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { ToolbarPopover } from '@kbn/shared-ux-button-toolbar'; import { type BaseVisType, VisGroups, type VisTypeAlias } from '@kbn/visualizations-plugin/public'; -import { SolutionToolbarPopover } from '@kbn/presentation-util-plugin/public'; import type { EmbeddableFactory } from '@kbn/embeddable-plugin/public'; import { pluginServices } from '../../services/plugin_services'; import { DASHBOARD_APP_ID } from '../../dashboard_constants'; @@ -261,13 +261,11 @@ export const EditorMenu = ({ createNewVisType, createNewEmbeddable }: Props) => ]; }; return ( - @@ -279,6 +277,6 @@ export const EditorMenu = ({ createNewVisType, createNewEmbeddable }: Props) => data-test-subj="dashboardEditorContextMenu" /> )} - + ); }; diff --git a/src/plugins/dashboard/tsconfig.json b/src/plugins/dashboard/tsconfig.json index ddf6e65028ea2..17ec86d25e6ef 100644 --- a/src/plugins/dashboard/tsconfig.json +++ b/src/plugins/dashboard/tsconfig.json @@ -53,6 +53,7 @@ "@kbn/core-execution-context-common", "@kbn/core-custom-branding-browser", "@kbn/shared-ux-router", + "@kbn/shared-ux-button-toolbar", ], "exclude": [ "target/**/*", diff --git a/src/plugins/data/common/search/expressions/esaggs/request_handler.ts b/src/plugins/data/common/search/expressions/esaggs/request_handler.ts index 1497dd9aa1a37..196ebfa55810f 100644 --- a/src/plugins/data/common/search/expressions/esaggs/request_handler.ts +++ b/src/plugins/data/common/search/expressions/esaggs/request_handler.ts @@ -61,13 +61,6 @@ export const handleRequest = ({ searchSource.setField('index', indexPattern); searchSource.setField('size', 0); - // Create a new search source that inherits the original search source - // but has the appropriate timeRange applied via a filter. - // This is a temporary solution until we properly pass down all required - // information for the request to the request handler (https://github.com/elastic/kibana/issues/16641). - // Using callParentStartHandlers: true we make sure, that the parent searchSource - // onSearchRequestStart will be called properly even though we use an inherited - // search source. const timeFilterSearchSource = searchSource.createChild({ callParentStartHandlers: true }); const requestSearchSource = timeFilterSearchSource.createChild({ callParentStartHandlers: true, diff --git a/src/plugins/data/server/search/saved_objects/search_session.ts b/src/plugins/data/server/search/saved_objects/search_session.ts index 5e3906cfd8f63..ef0f960c84e8c 100644 --- a/src/plugins/data/server/search/saved_objects/search_session.ts +++ b/src/plugins/data/server/search/saved_objects/search_session.ts @@ -64,4 +64,14 @@ export const searchSessionSavedObjectType: SavedObjectsType = { }, }, migrations: searchSessionSavedObjectMigrations, + excludeOnUpgrade: async () => { + return { + bool: { + must: [ + { term: { type: SEARCH_SESSION_TYPE } }, + { match: { 'search-session.persisted': false } }, + ], + }, + }; + }, }; diff --git a/src/plugins/data_view_editor/public/data_view_editor_service.ts b/src/plugins/data_view_editor/public/data_view_editor_service.ts index 4bfd04c912a6c..798e339b7ee3f 100644 --- a/src/plugins/data_view_editor/public/data_view_editor_service.ts +++ b/src/plugins/data_view_editor/public/data_view_editor_service.ts @@ -129,8 +129,6 @@ export class DataViewEditorService { private requireTimestampField: boolean; private type = INDEX_PATTERN_TYPE.DEFAULT; - // state - private state = { ...defaultDataViewEditorState }; private indexPattern = ''; private allowHidden = false; @@ -165,8 +163,7 @@ export class DataViewEditorService { private currentLoadingMatchedIndices = 0; private updateState = (newState: Partial) => { - this.state = { ...this.state, ...newState }; - this.state$.next(this.state); + this.state$.next({ ...this.state$.getValue(), ...newState }); }; private getRollupIndexCaps = async () => { @@ -324,7 +321,8 @@ export class DataViewEditorService { }; private loadTimestampFields = async () => { - if (this.state.matchedIndices.exactMatchedIndices.length === 0) { + const currentState = this.state$.getValue(); + if (currentState.matchedIndices.exactMatchedIndices.length === 0) { this.updateState({ timestampFieldOptions: [], loadingTimestampFields: false }); return; } @@ -335,7 +333,7 @@ export class DataViewEditorService { }; if (this.type === INDEX_PATTERN_TYPE.ROLLUP) { getFieldsOptions.type = INDEX_PATTERN_TYPE.ROLLUP; - getFieldsOptions.rollupIndex = this.state.rollupIndexName || ''; + getFieldsOptions.rollupIndex = currentState.rollupIndexName || ''; } let timestampFieldOptions: TimestampOption[] = []; diff --git a/src/plugins/data_view_field_editor/public/components/preview/preview_controller.ts b/src/plugins/data_view_field_editor/public/components/preview/preview_controller.ts index b94051bb0107d..80b11e74597aa 100644 --- a/src/plugins/data_view_field_editor/public/components/preview/preview_controller.ts +++ b/src/plugins/data_view_field_editor/public/components/preview/preview_controller.ts @@ -48,24 +48,18 @@ export class PreviewController { private dataView: DataView; // @ts-ignore private search: ISearchStart; - private state: PreviewState = previewStateDefault; private internalState$: BehaviorSubject; state$: BehaviorObservable; private updateState = (newState: Partial) => { - this.state = { ...this.state, ...newState }; - this.publishState(); - }; - - private publishState = () => { - // todo try removing object copy - this.internalState$.next({ ...this.state }); + this.internalState$.next({ ...this.state$.getValue(), ...newState }); }; togglePinnedField = (fieldName: string) => { + const currentState = this.state$.getValue(); const pinnedFields = { - ...this.state.pinnedFields, - [fieldName]: !this.state.pinnedFields[fieldName], + ...currentState.pinnedFields, + [fieldName]: !currentState.pinnedFields[fieldName], }; this.updateState({ pinnedFields }); @@ -84,18 +78,20 @@ export class PreviewController { }; goToNextDocument = () => { - if (this.state.currentIdx >= this.state.documents.length - 1) { + const currentState = this.state$.getValue(); + if (currentState.currentIdx >= currentState.documents.length - 1) { this.updateState({ currentIdx: 0 }); } else { - this.updateState({ currentIdx: this.state.currentIdx + 1 }); + this.updateState({ currentIdx: currentState.currentIdx + 1 }); } }; goToPreviousDocument = () => { - if (this.state.currentIdx === 0) { - this.updateState({ currentIdx: this.state.documents.length - 1 }); + const currentState = this.state$.getValue(); + if (currentState.currentIdx === 0) { + this.updateState({ currentIdx: currentState.documents.length - 1 }); } else { - this.updateState({ currentIdx: this.state.currentIdx - 1 }); + this.updateState({ currentIdx: currentState.currentIdx - 1 }); } }; diff --git a/src/plugins/data_views/common/data_views/data_views.ts b/src/plugins/data_views/common/data_views/data_views.ts index 65d23292486f3..13fbb9cec243e 100644 --- a/src/plugins/data_views/common/data_views/data_views.ts +++ b/src/plugins/data_views/common/data_views/data_views.ts @@ -583,7 +583,7 @@ export class DataViewsService { } catch (err) { if (err instanceof DataViewMissingIndices) { this.onNotification( - { title: err.message, color: 'danger', iconType: 'alert' }, + { title: err.message, color: 'danger', iconType: 'error' }, `refreshFields:${dataView.getIndexPattern()}` ); } @@ -637,7 +637,7 @@ export class DataViewsService { if (err instanceof DataViewMissingIndices) { if (displayErrors) { this.onNotification( - { title: err.message, color: 'danger', iconType: 'alert' }, + { title: err.message, color: 'danger', iconType: 'error' }, `refreshFieldSpecMap:${title}` ); } @@ -807,7 +807,7 @@ export class DataViewsService { { title: err.message, color: 'danger', - iconType: 'alert', + iconType: 'error', }, `initFromSavedObject:${spec.title}` ); diff --git a/src/plugins/discover/public/__mocks__/__storybook_mocks__/get_data_view_mock.tsx b/src/plugins/discover/public/__mocks__/__storybook_mocks__/get_data_view_mock.tsx index 718f43892aac2..ee6736519ca31 100644 --- a/src/plugins/discover/public/__mocks__/__storybook_mocks__/get_data_view_mock.tsx +++ b/src/plugins/discover/public/__mocks__/__storybook_mocks__/get_data_view_mock.tsx @@ -10,13 +10,6 @@ import { DataView } from '@kbn/data-views-plugin/common'; export function getDataViewMock(isTimebased = true) { const fields = [ - { - name: '_index', - type: 'string', - scripted: false, - filterable: true, - aggregatable: false, - }, { name: 'date', type: 'date', @@ -80,14 +73,16 @@ export function getDataViewMock(isTimebased = true) { getFormatterForField: () => ({ convert: (name: string) => name, }), - getFieldByName: () => { - return fields[0]; + getFieldByName: (name: string) => { + return fields.find((field) => field.name === name); }, + getIndexPattern: () => 'test', metaFields: [], timeFieldName: isTimebased ? 'date' : undefined, + isPersisted: () => true, } as unknown as DataView; - dataViewMock.fields.getByName = () => fields[0]; + dataViewMock.fields.getByName = (name: string) => fields.find((field) => field.name === name); dataViewMock.fields.getAll = () => fields; return dataViewMock; } diff --git a/src/plugins/discover/public/__mocks__/__storybook_mocks__/with_discover_services.tsx b/src/plugins/discover/public/__mocks__/__storybook_mocks__/with_discover_services.tsx index d8309e452f7a1..975317e918cb3 100644 --- a/src/plugins/discover/public/__mocks__/__storybook_mocks__/with_discover_services.tsx +++ b/src/plugins/discover/public/__mocks__/__storybook_mocks__/with_discover_services.tsx @@ -29,6 +29,7 @@ import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { Plugin as NavigationPublicPlugin } from '@kbn/navigation-plugin/public'; import { SearchBar, UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import { SavedQuery } from '@kbn/data-plugin/public'; +import { BehaviorSubject, Observable } from 'rxjs'; const NavigationPlugin = new NavigationPublicPlugin({} as PluginInitializerContext); @@ -57,8 +58,18 @@ export const uiSettingsMock = { }, } as unknown as IUiSettingsClient; -const services = { - core: { http: { basePath: { prepend: () => void 0 } } }, +const filterManager = { + getGlobalFilters: () => [], + getAppFilters: () => [], + getFetches$: () => new Observable(), +}; + +export const services = { + core: { + http: { basePath: { prepend: () => void 0 } }, + notifications: { toasts: {} }, + docLinks: { links: { discover: {} } }, + }, storage: new LocalStorageMock({ [SIDEBAR_CLOSED_KEY]: false, }) as unknown as Storage, @@ -74,12 +85,53 @@ const services = { from: 'now-7d', to: 'now', }), + getRefreshInterval: () => ({}), + getFetch$: () => new Observable(), + getAutoRefreshFetch$: () => new Observable(), + calculateBounds: () => ({ min: undefined, max: undefined }), + getTimeDefaults: () => ({}), + createFilter: () => ({}), }, }, savedQueries: { findSavedQueries: () => Promise.resolve({ queries: [] as SavedQuery[] }) }, + queryString: { + getDefaultQuery: () => { + return { query: '', language: 'kuery' }; + }, + getUpdates$: () => new Observable(), + }, + filterManager, + getState: () => { + return { + filters: [], + query: { query: '', language: 'kuery' }, + }; + }, + state$: new Observable(), + }, + search: { + session: { + getSession$: () => { + return new BehaviorSubject('test').asObservable(); + }, + state$: new Observable(), + }, + searchSource: { + createEmpty: () => { + const empty = { + setField: () => { + return empty; + }, + fetch$: () => new Observable(), + }; + return empty; + }, + }, }, dataViews: { getIdsWithTitle: () => Promise.resolve([]), + get: () => Promise.resolve({}), + find: () => Promise.resolve([]), }, }, uiSettings: uiSettingsMock, @@ -120,10 +172,7 @@ const services = { }, docLinks: { links: { discover: {} } }, addBasePath: (path: string) => path, - filterManager: { - getGlobalFilters: () => [], - getAppFilters: () => [], - }, + filterManager, history: () => ({}), fieldFormats: { deserialize: () => { @@ -134,6 +183,15 @@ const services = { toastNotifications: { addInfo: action('add toast'), }, + lens: { + EmbeddableComponent:
    Histogram
    , + }, + unifiedSearch: { + autocomplete: { + hasQuerySuggestions: () => Promise.resolve([]), + getQuerySuggestions: () => Promise.resolve([]), + }, + }, } as unknown as DiscoverServices; export const withDiscoverServices = (Component: FunctionComponent) => { diff --git a/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_table.tsx b/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_table.tsx index b01a05f932e13..0bb6947dc7674 100644 --- a/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_table.tsx +++ b/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_table.tsx @@ -138,7 +138,7 @@ export const FieldStatisticsTable = (props: FieldStatisticsTableProps) => { const availableFields$ = stateContainer?.dataState.data$.availableFields$; const sub = embeddable?.getOutput$().subscribe((output: DataVisualizerGridEmbeddableOutput) => { if (output.showDistributions !== undefined && stateContainer) { - stateContainer.setAppState({ hideAggregatedPreview: !output.showDistributions }); + stateContainer.appState.update({ hideAggregatedPreview: !output.showDistributions }); } }); diff --git a/src/plugins/discover/public/application/main/components/layout/__stories__/discover_layout.stories.tsx b/src/plugins/discover/public/application/main/components/layout/__stories__/discover_layout.stories.tsx index 8392bb7c0ed49..d661a85289a76 100644 --- a/src/plugins/discover/public/application/main/components/layout/__stories__/discover_layout.stories.tsx +++ b/src/plugins/discover/public/application/main/components/layout/__stories__/discover_layout.stories.tsx @@ -9,6 +9,7 @@ import React, { useState } from 'react'; import { storiesOf } from '@storybook/react'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; +import { DiscoverMainProvider } from '../../../services/discover_state_provider'; import { AppState } from '../../../services/discover_app_state_container'; import { getDataViewMock } from '../../../../../__mocks__/__storybook_mocks__/get_data_view_mock'; import { withDiscoverServices } from '../../../../../__mocks__/__storybook_mocks__/with_discover_services'; @@ -22,7 +23,7 @@ setHeaderActionMenuMounter(() => void 0); const DiscoverLayoutStory = (layoutProps: DiscoverLayoutProps) => { const [state, setState] = useState({}); - const setAppState = (newState: Partial) => { + const update = (newState: Partial) => { setState((prevState) => ({ ...prevState, ...newState })); }; @@ -33,8 +34,7 @@ const DiscoverLayoutStory = (layoutProps: DiscoverLayoutProps) => { {...layoutProps} stateContainer={{ ...layoutProps.stateContainer, - appState: { ...layoutProps.stateContainer.appState, getState }, - setAppState, + appState: { ...layoutProps.stateContainer.appState, getState, update }, }} /> ); @@ -42,27 +42,42 @@ const DiscoverLayoutStory = (layoutProps: DiscoverLayoutProps) => { storiesOf('components/layout/DiscoverLayout', module).add( 'Data view with timestamp', - withDiscoverServices(() => ( - - - - )) + withDiscoverServices(() => { + const props = getDocumentsLayoutProps(getDataViewMock(true)); + return ( + + + + + + ); + }) ); storiesOf('components/layout/DiscoverLayout', module).add( 'Data view without timestamp', - withDiscoverServices(() => ( - - - - )) + withDiscoverServices(() => { + const props = getDocumentsLayoutProps(getDataViewMock(false)); + return ( + + + + + + ); + }) ); storiesOf('components/layout/DiscoverLayout', module).add( 'SQL view', - withDiscoverServices(() => ( - - - - )) + withDiscoverServices(() => { + const props = getPlainRecordLayoutProps(getDataViewMock(false)); + return ( + + + + + + ); + }) ); diff --git a/src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts b/src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts index 3d7e72b32bb12..1082a5b127397 100644 --- a/src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts +++ b/src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts @@ -6,11 +6,12 @@ * Side Public License, v 1. */ -import { DataView, DataViewAttributes, SavedObject } from '@kbn/data-views-plugin/common'; +import { DataView } from '@kbn/data-views-plugin/common'; import { SearchSource } from '@kbn/data-plugin/common'; -import { BehaviorSubject, Subject } from 'rxjs'; +import { BehaviorSubject, Observable, Subject } from 'rxjs'; import { RequestAdapter } from '@kbn/inspector-plugin/common'; import { action } from '@storybook/addon-actions'; +import { createHashHistory } from 'history'; import { FetchStatus } from '../../../../types'; import { AvailableFields$, @@ -23,7 +24,11 @@ import { buildDataTableRecordList } from '../../../../../utils/build_data_record import { esHits } from '../../../../../__mocks__/es_hits'; import { SavedSearch } from '../../../../..'; import { DiscoverLayoutProps } from '../types'; -import { DiscoverStateContainer } from '../../../services/discover_state'; +import { + DiscoverStateContainer, + getDiscoverStateContainer, +} from '../../../services/discover_state'; +import { services } from '../../../../../__mocks__/__storybook_mocks__/with_discover_services'; const documentObservables = { main$: new BehaviorSubject({ @@ -45,6 +50,7 @@ const documentObservables = { fetchStatus: FetchStatus.COMPLETE, result: Number(esHits.length), }) as DataTotalHits$, + fetch$: new Observable(), }; const plainRecordObservables = { @@ -72,17 +78,11 @@ const plainRecordObservables = { }) as DataTotalHits$, }; -const getCommonProps = (dataView: DataView) => { +const getCommonProps = () => { const searchSourceMock = {} as unknown as SearchSource; - const dataViewList = [dataView].map((ip) => { - return { ...ip, ...{ attributes: { title: ip.getIndexPattern() } } }; - }) as unknown as Array>; - const savedSearchMock = {} as unknown as SavedSearch; return { - dataView, - dataViewList, inspectorAdapters: { requests: new RequestAdapter() }, navigateTo: action('navigate to somewhere nice'), onChangeDataView: action('change the data view'), @@ -104,33 +104,70 @@ const getCommonProps = (dataView: DataView) => { }; }; -export function getDocumentsLayoutProps(dataView: DataView) { +function getSavedSearch(dataView: DataView) { return { - ...getCommonProps(dataView), - savedSearchData$: documentObservables, - state: { - columns: ['name', 'message', 'bytes'], - sort: [['date', 'desc']], - query: { - language: 'kuery', - query: '', + searchSource: { + getField: (value: string) => { + if (value === 'index') { + return dataView; + } + }, + getOwnField: () => { + return { + query: '', + }; + }, + createChild: () => { + return { + fetch$: () => new Observable(), + } as unknown as SearchSource; }, - filters: [], }, + } as unknown as SavedSearch; +} + +export function getDocumentsLayoutProps(dataView: DataView) { + const stateContainer = getDiscoverStateContainer({ + history: createHashHistory(), + savedSearch: getSavedSearch(dataView), + services, + }); + stateContainer.appState.set({ + columns: ['name', 'message', 'bytes'], + sort: dataView.timeFieldName ? [['date', 'desc']] : [['name', 'desc']], + query: { + language: 'kuery', + query: '', + }, + filters: [], + hideChart: true, + }); + stateContainer.actions.setDataView(dataView); + stateContainer.dataState.data$ = documentObservables; + return { + ...getCommonProps(), + stateContainer, } as unknown as DiscoverLayoutProps; } export const getPlainRecordLayoutProps = (dataView: DataView) => { - return { - ...getCommonProps(dataView), - savedSearchData$: plainRecordObservables, - state: { - columns: ['name', 'message', 'bytes'], - sort: [['date', 'desc']], - query: { - sql: 'SELECT * FROM "kibana_sample_data_ecommerce"', - }, - filters: [], + const stateContainer = getDiscoverStateContainer({ + history: createHashHistory(), + savedSearch: getSavedSearch(dataView), + services, + }); + stateContainer.appState.set({ + columns: ['name', 'message', 'bytes'], + sort: [['date', 'desc']], + query: { + sql: 'SELECT * FROM "kibana_sample_data_ecommerce"', }, + filters: [], + }); + stateContainer.actions.setDataView(dataView); + stateContainer.dataState.data$ = plainRecordObservables; + return { + ...getCommonProps(), + stateContainer, } as unknown as DiscoverLayoutProps; }; diff --git a/src/plugins/discover/public/application/main/components/layout/discover_documents.test.tsx b/src/plugins/discover/public/application/main/components/layout/discover_documents.test.tsx index d73c1163c8a0f..ab45bb1ce606d 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_documents.test.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_documents.test.tsx @@ -12,7 +12,6 @@ import { mountWithIntl } from '@kbn/test-jest-helpers'; import { setHeaderActionMenuMounter } from '../../../../kibana_services'; import { esHits } from '../../../../__mocks__/es_hits'; import { savedSearchMock } from '../../../../__mocks__/saved_search'; -import { DiscoverStateContainer } from '../../services/discover_state'; import { DataDocuments$ } from '../../services/discover_data_state_container'; import { discoverServiceMock } from '../../../../__mocks__/services'; import { FetchStatus } from '../../../types'; @@ -38,7 +37,7 @@ function mountComponent(fetchStatus: FetchStatus, hits: EsHitRecord[]) { result: hits.map((hit) => buildDataTableRecord(hit, dataViewMock)), }) as DataDocuments$; const stateContainer = getDiscoverStateMock({}); - stateContainer.setAppState({ index: dataViewMock.id }); + stateContainer.appState.update({ index: dataViewMock.id }); stateContainer.dataState.data$.documents$ = documents$; const props = { @@ -83,26 +82,20 @@ describe('Discover documents layout', () => { }); test('should set rounded width to state on resize column', () => { - let state = { + const state = { grid: { columns: { timestamp: { width: 173 }, someField: { width: 197 } } }, } as AppState; - const stateContainer = { - setAppState: (newState: Partial) => { - state = { ...state, ...newState }; - }, - appState: { - getState: () => state, - }, - } as unknown as DiscoverStateContainer; + const container = getDiscoverStateMock({}); + container.appState.update(state); onResize( { columnId: 'someField', width: 205.5435345534, }, - stateContainer + container ); - expect(state.grid?.columns?.someField.width).toEqual(206); + expect(container.appState.getState().grid?.columns?.someField.width).toEqual(206); }); }); 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 647a6561b7cf0..2a9698050eade 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 @@ -55,7 +55,7 @@ export const onResize = ( width: Math.round(colSettings.width), }; const newGrid = { ...grid, columns: newColumns }; - stateContainer.setAppState({ grid: newGrid }); + stateContainer.appState.update({ grid: newGrid }); }; function DiscoverDocumentsComponent({ @@ -123,7 +123,7 @@ function DiscoverDocumentsComponent({ config: uiSettings, dataView, dataViews, - setAppState: stateContainer.setAppState, + setAppState: stateContainer.appState.update, useNewFieldsApi, columns, sort, @@ -136,21 +136,21 @@ function DiscoverDocumentsComponent({ const onUpdateRowsPerPage = useCallback( (nextRowsPerPage: number) => { - stateContainer.setAppState({ rowsPerPage: nextRowsPerPage }); + stateContainer.appState.update({ rowsPerPage: nextRowsPerPage }); }, [stateContainer] ); const onSort = useCallback( (nextSort: string[][]) => { - stateContainer.setAppState({ sort: nextSort }); + stateContainer.appState.update({ sort: nextSort }); }, [stateContainer] ); const onUpdateRowHeight = useCallback( (newRowHeight: number) => { - stateContainer.setAppState({ rowHeight: newRowHeight }); + stateContainer.appState.update({ rowHeight: newRowHeight }); }, [stateContainer] ); 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 2396325ac82cc..cc975e9008686 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 @@ -39,13 +39,11 @@ import { act } from 'react-dom/test-utils'; function getStateContainer() { const stateContainer = getDiscoverStateMock({ isTimeBased: true }); - stateContainer.setAppState({ + stateContainer.appState.update({ interval: 'auto', hideChart: false, }); - stateContainer.setAppState = jest.fn(); - return stateContainer; } @@ -130,9 +128,9 @@ const mountComponent = async ({ onAddFilter: jest.fn(), resetSavedSearch, resizeRef: { current: null }, - searchSessionManager: createSearchSessionMock(session).searchSessionManager, inspectorAdapters: { requests: new RequestAdapter() }, }; + stateContainer.searchSessionManager = createSearchSessionMock(session).searchSessionManager; const coreTheme$ = new BehaviorSubject({ darkMode: 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 68a5d8e1c7ee9..5a74e58184ff0 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 @@ -11,7 +11,6 @@ import { UnifiedHistogramContainer } from '@kbn/unified-histogram-plugin/public' import { css } from '@emotion/react'; import useObservable from 'react-use/lib/useObservable'; import { useDiscoverHistogram } from './use_discover_histogram'; -import type { DiscoverSearchSessionManager } from '../../services/discover_search_session'; import type { InspectorAdapters } from '../../hooks/use_inspector'; import { type DiscoverMainContentProps, DiscoverMainContent } from './discover_main_content'; import { ResetSearchButton } from './reset_search_button'; @@ -20,7 +19,6 @@ export interface DiscoverHistogramLayoutProps extends DiscoverMainContentProps { resetSavedSearch: () => void; resizeRef: RefObject; inspectorAdapters: InspectorAdapters; - searchSessionManager: DiscoverSearchSessionManager; } const histogramLayoutCss = css` @@ -35,7 +33,6 @@ export const DiscoverHistogramLayout = ({ stateContainer, resizeRef, inspectorAdapters, - searchSessionManager, ...mainContentProps }: DiscoverHistogramLayoutProps) => { const commonProps = { @@ -43,8 +40,7 @@ export const DiscoverHistogramLayout = ({ stateContainer, savedSearchData$: stateContainer.dataState.data$, }; - - const searchSessionId = useObservable(searchSessionManager.searchSessionId$); + const searchSessionId = useObservable(stateContainer.searchSessionManager.searchSessionId$); const { hideChart, setUnifiedHistogramApi } = useDiscoverHistogram({ inspectorAdapters, 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 f14d89ead5b81..ec9360b3bb1f0 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 @@ -107,7 +107,7 @@ async function mountComponent( session.getSession$.mockReturnValue(new BehaviorSubject('123')); - stateContainer.setAppState({ interval: 'auto', query }); + stateContainer.appState.update({ interval: 'auto', query }); stateContainer.internalState.transitions.setDataView(dataView); const props = { @@ -124,9 +124,9 @@ async function mountComponent( setExpandedDoc: jest.fn(), persistDataView: jest.fn(), updateAdHocDataViewId: jest.fn(), - searchSessionManager: createSearchSessionMock(session).searchSessionManager, updateDataViewList: jest.fn(), }; + stateContainer.searchSessionManager = createSearchSessionMock(session).searchSessionManager; const component = mountWithIntl( 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 da47f1c1e9626..3e7bb1bc891b7 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 @@ -66,7 +66,6 @@ export function DiscoverLayout({ stateContainer, persistDataView, updateAdHocDataViewId, - searchSessionManager, updateDataViewList, }: DiscoverLayoutProps) { const { @@ -137,7 +136,7 @@ export function DiscoverLayout({ config: uiSettings, dataView, dataViews, - setAppState: stateContainer.setAppState, + setAppState: stateContainer.appState.update, useNewFieldsApi, columns, sort, @@ -251,7 +250,6 @@ export function DiscoverLayout({ onFieldEdited={onFieldEdited} resizeRef={resizeRef} inspectorAdapters={inspectorAdapters} - searchSessionManager={searchSessionManager} /> {resultState === 'loading' && } @@ -272,12 +270,10 @@ export function DiscoverLayout({ resetSavedSearch, resultState, savedSearch, - searchSessionManager, setExpandedDoc, stateContainer, viewMode, ]); - return (

    { - stateContainer.setAppState({ viewMode: mode }); + stateContainer.appState.update({ viewMode: mode }); if (trackUiMetric) { if (mode === VIEW_MODE.AGGREGATED_LEVEL) { diff --git a/src/plugins/discover/public/application/main/components/layout/types.ts b/src/plugins/discover/public/application/main/components/layout/types.ts index 0d9b0c37e3009..d84b6807c080e 100644 --- a/src/plugins/discover/public/application/main/components/layout/types.ts +++ b/src/plugins/discover/public/application/main/components/layout/types.ts @@ -12,7 +12,6 @@ import type { ISearchSource } from '@kbn/data-plugin/public'; import { SavedSearch } from '@kbn/saved-search-plugin/public'; import { DataTableRecord } from '../../../../types'; import { DiscoverStateContainer } from '../../services/discover_state'; -import type { DiscoverSearchSessionManager } from '../../services/discover_search_session'; import type { InspectorAdapters } from '../../hooks/use_inspector'; export interface DiscoverLayoutProps { @@ -31,6 +30,5 @@ export interface DiscoverLayoutProps { stateContainer: DiscoverStateContainer; persistDataView: (dataView: DataView) => Promise; updateAdHocDataViewId: (dataView: DataView) => Promise; - searchSessionManager: DiscoverSearchSessionManager; updateDataViewList: (newAdHocDataViews: DataView[]) => void; } diff --git a/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.test.tsx b/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.test.tsx index 8463b380efc8f..e69da4b4178d4 100644 --- a/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.test.tsx +++ b/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.test.tsx @@ -86,14 +86,16 @@ const mockCheckHitCount = checkHitCount as jest.MockedFunction { const getStateContainer = () => { const stateContainer = getDiscoverStateMock({ isTimeBased: true }); - stateContainer.setAppState({ + stateContainer.appState.update({ interval: 'auto', hideChart: false, breakdownField: 'extension', }); - const wrappedStateContainer = Object.create(stateContainer); - wrappedStateContainer.setAppState = jest.fn((newState) => stateContainer.setAppState(newState)); - return wrappedStateContainer; + const appState = stateContainer.appState; + const wrappedStateContainer = Object.create(appState); + wrappedStateContainer.update = jest.fn((newState) => appState.update(newState)); + stateContainer.appState = wrappedStateContainer; + return stateContainer; }; const renderUseDiscoverHistogram = async ({ @@ -222,7 +224,7 @@ describe('useDiscoverHistogram', () => { hook.result.current.setUnifiedHistogramApi(api); }); expect(inspectorAdapters.lensRequests).toBe(lensRequestAdapter); - expect(stateContainer.setAppState).toHaveBeenCalledWith({ + expect(stateContainer.appState.update).toHaveBeenCalledWith({ interval: state.timeInterval, hideChart: state.chartHidden, breakdownField: state.breakdownField, @@ -245,7 +247,7 @@ describe('useDiscoverHistogram', () => { act(() => { hook.result.current.setUnifiedHistogramApi(api); }); - expect(stateContainer.setAppState).not.toHaveBeenCalled(); + expect(stateContainer.appState.update).not.toHaveBeenCalled(); }); it('should sync the state container state with Unified Histogram', async () => { @@ -446,10 +448,10 @@ describe('useDiscoverHistogram', () => { hook.result.current.setUnifiedHistogramApi(api); }); act(() => { - stateContainer.setAppState({ hideChart: true }); + stateContainer.appState.update({ hideChart: true }); }); act(() => { - stateContainer.setAppState({ hideChart: false }); + stateContainer.appState.update({ hideChart: false }); }); act(() => { savedSearchFetch$.next({ reset: false, searchSessionId: '1234' }); diff --git a/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.ts b/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.ts index d259252f18686..a74f0d4358ea6 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 @@ -122,7 +122,7 @@ export const useDiscoverHistogram = ({ }; if (!isEqual(oldState, newState)) { - stateContainer.setAppState(newState); + stateContainer.appState.update(newState); } }); diff --git a/src/plugins/discover/public/application/main/components/sidebar/deprecated_stats/discover_field_details.test.tsx b/src/plugins/discover/public/application/main/components/sidebar/deprecated_stats/discover_field_details.test.tsx index e31445344b82b..17945e7e3e027 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/deprecated_stats/discover_field_details.test.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/deprecated_stats/discover_field_details.test.tsx @@ -8,8 +8,10 @@ import React from 'react'; import { findTestSubject } from '@elastic/eui/lib/test'; +import { ReactWrapper } from 'enzyme'; import { mountWithIntl } from '@kbn/test-jest-helpers'; - +import { EuiLoadingSpinner } from '@elastic/eui'; +import { act } from 'react-dom/test-utils'; import { DiscoverFieldDetails } from './discover_field_details'; import { DataViewField } from '@kbn/data-views-plugin/public'; import { stubDataView, stubLogstashDataView } from '@kbn/data-views-plugin/common/data_view.stub'; @@ -52,4 +54,57 @@ describe('discover sidebar field details', function () { onAddButton.simulate('click'); expect(onAddFilter).toHaveBeenCalledWith('_exists_', visualizableField.name, '+'); }); + + it('should stay in sync with documents$ state', async function () { + const testDocuments$ = new BehaviorSubject({ + fetchStatus: FetchStatus.LOADING, + }) as DataDocuments$; + const visualizableField = new DataViewField({ + name: 'bytes', + type: 'number', + esTypes: ['long'], + count: 10, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }); + let component: ReactWrapper; + + await act(async () => { + component = await mountWithIntl( + + ); + }); + + expect(component!.find(EuiLoadingSpinner).exists()).toBeTruthy(); + + await act(async () => { + testDocuments$.next({ + fetchStatus: FetchStatus.COMPLETE, + result: hits, + }); + }); + + await component!.update(); + + expect(component!.find(EuiLoadingSpinner).exists()).toBeFalsy(); + expect( + findTestSubject(component!, `discoverFieldDetails-${visualizableField.name}`).exists() + ).toBeTruthy(); + + await act(async () => { + testDocuments$.next({ + fetchStatus: FetchStatus.UNINITIALIZED, + }); + }); + + await component!.update(); + + expect(component!.isEmptyRender()).toBeTruthy(); + }); }); diff --git a/src/plugins/discover/public/application/main/components/sidebar/deprecated_stats/discover_field_details.tsx b/src/plugins/discover/public/application/main/components/sidebar/deprecated_stats/discover_field_details.tsx index 0a3d1b5c58072..aa027376d9155 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/deprecated_stats/discover_field_details.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/deprecated_stats/discover_field_details.tsx @@ -6,13 +6,13 @@ * Side Public License, v 1. */ -import React, { useMemo } from 'react'; +import React, { useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiText, EuiSpacer, EuiLink, EuiTitle } from '@elastic/eui'; +import { EuiLink, EuiSpacer, EuiText, EuiTitle, EuiLoadingSpinner } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { DataViewField, DataView } from '@kbn/data-views-plugin/public'; +import { DataView, DataViewField } from '@kbn/data-views-plugin/public'; import { DiscoverFieldBucket } from './discover_field_bucket'; -import { Bucket } from './types'; +import { Bucket, FieldDetails } from './types'; import { getDetails, isValidFieldDetails } from './get_details'; import { FetchStatus } from '../../../../types'; import { DataDocuments$ } from '../../../services/discover_data_state_container'; @@ -33,12 +33,30 @@ export function DiscoverFieldDetails({ dataView, onAddFilter, }: DiscoverFieldDetailsProps) { - const details = useMemo(() => { - const data = documents$.getValue(); - const documents = data.fetchStatus === FetchStatus.COMPLETE ? data.result : undefined; - return getDetails(field, documents, dataView); - }, [field, documents$, dataView]); + const [detailsState, setDetailsState] = useState<{ + details?: FieldDetails; + loaded: boolean; + }>(); + useEffect(() => { + const subscription = documents$.subscribe((data) => { + if (data.fetchStatus === FetchStatus.COMPLETE) { + setDetailsState({ details: getDetails(field, data.result, dataView), loaded: true }); + } else { + setDetailsState({ details: undefined, loaded: data.fetchStatus !== FetchStatus.LOADING }); + } + }); + + return () => { + subscription.unsubscribe(); + }; + }, [documents$, setDetailsState, dataView, field]); + + if (!detailsState?.loaded) { + return ; + } + + const details = detailsState?.details; if (!details) { return null; } 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 f8aae447c5c98..bf4b608a3c017 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 @@ -199,9 +199,9 @@ export const DiscoverTopNav = ({ }; const updateSavedQueryId = (newSavedQueryId: string | undefined) => { - const { appState, setAppState } = stateContainer; + const { appState } = stateContainer; if (newSavedQueryId) { - setAppState({ savedQuery: newSavedQueryId }); + appState.update({ savedQuery: newSavedQueryId }); } else { // remove savedQueryId from state const newState = { 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 50caf6aa15e68..828558cec13ae 100644 --- a/src/plugins/discover/public/application/main/discover_main_app.tsx +++ b/src/plugins/discover/public/application/main/discover_main_app.tsx @@ -57,7 +57,6 @@ export function DiscoverMainApp(props: DiscoverMainProps) { resetSavedSearch, searchSource, stateContainer, - searchSessionManager, updateDataViewList, } = useDiscoverState({ services, @@ -119,7 +118,6 @@ export function DiscoverMainApp(props: DiscoverMainProps) { stateContainer={stateContainer} persistDataView={persistDataView} updateAdHocDataViewId={updateAdHocDataViewId} - searchSessionManager={searchSessionManager} updateDataViewList={updateDataViewList} /> diff --git a/src/plugins/discover/public/application/main/hooks/use_discover_state.ts b/src/plugins/discover/public/application/main/hooks/use_discover_state.ts index 1170629142dae..fe9612af0ca9f 100644 --- a/src/plugins/discover/public/application/main/hooks/use_discover_state.ts +++ b/src/plugins/discover/public/application/main/hooks/use_discover_state.ts @@ -120,7 +120,7 @@ export function useDiscoverState({ * or dataView / savedSearch switch */ useEffect(() => { - const stopSync = stateContainer.initializeAndSync(dataView, filterManager, data); + const stopSync = stateContainer.actions.initializeAndSync(dataView, filterManager, data); setState(stateContainer.appState.getState()); return () => stopSync(); @@ -213,7 +213,7 @@ export function useDiscoverState({ */ useEffect(() => { if (dataView && (!dataView.isTimeBased() || dataView.type === DataViewType.ROLLUP)) { - stateContainer.pauseAutoRefreshInterval(); + stateContainer.actions.pauseAutoRefreshInterval(); } }, [dataView, stateContainer]); @@ -226,7 +226,6 @@ export function useDiscoverState({ stateContainer, persistDataView, updateAdHocDataViewId, - searchSessionManager, updateDataViewList, }; } diff --git a/src/plugins/discover/public/application/main/hooks/use_test_based_query_language.test.ts b/src/plugins/discover/public/application/main/hooks/use_test_based_query_language.test.ts index b596e2bc06c40..7ac2fb6ef7916 100644 --- a/src/plugins/discover/public/application/main/hooks/use_test_based_query_language.test.ts +++ b/src/plugins/discover/public/application/main/hooks/use_test_based_query_language.test.ts @@ -28,7 +28,7 @@ function getHookProps( const replaceUrlState = jest.fn(); const stateContainer = getDiscoverStateMock({ isTimeBased: true }); stateContainer.appState.replaceUrlState = replaceUrlState; - stateContainer.setAppState({ columns: [] }); + stateContainer.appState.update({ columns: [] }); stateContainer.internalState.transitions.setSavedDataViews([dataViewMock as DataViewListItem]); const msgLoading = { diff --git a/src/plugins/discover/public/application/main/services/discover_state.test.ts b/src/plugins/discover/public/application/main/services/discover_state.test.ts index a61808d433b5b..c2efd78b24c8f 100644 --- a/src/plugins/discover/public/application/main/services/discover_state.test.ts +++ b/src/plugins/discover/public/application/main/services/discover_state.test.ts @@ -48,7 +48,7 @@ describe('Test discover state', () => { stopSync = () => {}; }); test('setting app state and syncing to URL', async () => { - state.setAppState({ index: 'modified' }); + state.appState.update({ index: 'modified' }); state.kbnUrlStateStorage.kbnUrlControls.flush(); expect(getCurrentUrl()).toMatchInlineSnapshot( `"/#?_a=(columns:!(default_column),index:modified,interval:auto,sort:!())"` @@ -72,23 +72,23 @@ describe('Test discover state', () => { }); test('isAppStateDirty returns whether the current state has changed', async () => { - state.setAppState({ index: 'modified' }); + state.appState.update({ index: 'modified' }); expect(state.appState.hasChanged()).toBeTruthy(); state.appState.resetInitialState(); expect(state.appState.hasChanged()).toBeFalsy(); }); test('getPreviousAppState returns the state before the current', async () => { - state.setAppState({ index: 'first' }); + state.appState.update({ index: 'first' }); const stateA = state.appState.getState(); - state.setAppState({ index: 'second' }); + state.appState.update({ index: 'second' }); expect(state.appState.getPrevious()).toEqual(stateA); }); test('pauseAutoRefreshInterval sets refreshInterval.pause to true', async () => { history.push('/#?_g=(refreshInterval:(pause:!f,value:5000))'); expect(getCurrentUrl()).toBe('/#?_g=(refreshInterval:(pause:!f,value:5000))'); - await state.pauseAutoRefreshInterval(); + await state.actions.pauseAutoRefreshInterval(); expect(getCurrentUrl()).toBe('/#?_g=(refreshInterval:(pause:!t,value:5000))'); }); }); diff --git a/src/plugins/discover/public/application/main/services/discover_state.ts b/src/plugins/discover/public/application/main/services/discover_state.ts index 934c599be785f..1a6350f865ef4 100644 --- a/src/plugins/discover/public/application/main/services/discover_state.ts +++ b/src/plugins/discover/public/application/main/services/discover_state.ts @@ -73,26 +73,14 @@ export interface DiscoverStateContainer { * Data fetching related state **/ dataState: DataStateContainer; - /** - * Initialize state with filters and query, start state syncing - */ - initializeAndSync: ( - dataView: DataView, - filterManager: FilterManager, - data: DataPublicPluginStart - ) => () => void; - /** - * Set app state to with a partial new app state - */ - setAppState: (newState: Partial) => void; - /** - * Pause the auto refresh interval without pushing an entry to history - */ - pauseAutoRefreshInterval: () => Promise; /** * functions executed by UI */ actions: { + /** + * Pause the auto refresh interval without pushing an entry to history + */ + pauseAutoRefreshInterval: () => Promise; /** * Set the currently selected data view */ @@ -132,6 +120,14 @@ export interface DiscoverStateContainer { * @param dataView */ replaceAdHocDataViewWithId: (id: string, dataView: DataView) => void; + /** + * Initialize state with filters and query, start state syncing + */ + initializeAndSync: ( + dataView: DataView, + filterManager: FilterManager, + data: DataPublicPluginStart + ) => () => void; }; } @@ -214,6 +210,7 @@ export function getDiscoverStateContainer({ ); return { fallback: !nextDataViewData.stateValFound, dataView: nextDataView }; }; + const initializeAndSync = () => appStateContainer.initAndSync(savedSearch); return { kbnUrlStateStorage: stateStorage, @@ -221,10 +218,8 @@ export function getDiscoverStateContainer({ internalState: internalStateContainer, dataState: dataStateContainer, searchSessionManager, - setAppState: (newPartial: AppState) => appStateContainer.update(newPartial), - pauseAutoRefreshInterval, - initializeAndSync: () => appStateContainer.initAndSync(savedSearch), actions: { + pauseAutoRefreshInterval, setDataView, loadAndResolveDataView, loadDataViewList, @@ -232,6 +227,7 @@ export function getDiscoverStateContainer({ appendAdHocDataViews, replaceAdHocDataViewWithId, removeAdHocDataViewById, + initializeAndSync, }, }; } diff --git a/src/plugins/discover/public/components/doc_table/actions/columns.ts b/src/plugins/discover/public/components/doc_table/actions/columns.ts index b6438a2e1cdd1..75c52356c88fb 100644 --- a/src/plugins/discover/public/components/doc_table/actions/columns.ts +++ b/src/plugins/discover/public/components/doc_table/actions/columns.ts @@ -8,8 +8,8 @@ import { Capabilities, IUiSettingsClient } from '@kbn/core/public'; import { DataViewsContract } from '@kbn/data-plugin/public'; import { DataView } from '@kbn/data-views-plugin/public'; +import { DiscoverAppStateContainer } from '../../../application/main/services/discover_app_state_container'; import { SORT_DEFAULT_ORDER_SETTING } from '../../../../common'; -import { DiscoverStateContainer as DiscoverGetStateReturn } from '../../../application/main/services/discover_state'; import { GetStateReturn as ContextGetStateReturn } from '../../../application/context/services/context_state'; import { popularizeField } from '../../../utils/popularize_field'; @@ -70,7 +70,7 @@ export function getStateColumnActions({ dataView: DataView; dataViews: DataViewsContract; useNewFieldsApi: boolean; - setAppState: DiscoverGetStateReturn['setAppState'] | ContextGetStateReturn['setAppState']; + setAppState: DiscoverAppStateContainer['update'] | ContextGetStateReturn['setAppState']; columns?: string[]; sort: string[][] | undefined; }) { diff --git a/src/plugins/discover/public/components/doc_table/doc_table_wrapper.test.tsx b/src/plugins/discover/public/components/doc_table/doc_table_wrapper.test.tsx index 2eca5a90b9fbf..62fdccc0f4642 100644 --- a/src/plugins/discover/public/components/doc_table/doc_table_wrapper.test.tsx +++ b/src/plugins/discover/public/components/doc_table/doc_table_wrapper.test.tsx @@ -7,35 +7,33 @@ */ import React from 'react'; +import { EuiIcon, EuiLoadingSpinner } from '@elastic/eui'; import { findTestSubject, mountWithIntl } from '@kbn/test-jest-helpers'; import { dataViewMock } from '../../__mocks__/data_view'; -import { DocTableWrapper } from './doc_table_wrapper'; +import { DocTableWrapper, DocTableWrapperProps } from './doc_table_wrapper'; import { discoverServiceMock } from '../../__mocks__/services'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { buildDataTableRecord } from '../../utils/build_data_record'; import { EsHitRecord } from '../../types'; describe('Doc table component', () => { - const mountComponent = (rows?: EsHitRecord[]) => { + const mountComponent = (customProps?: Partial) => { const props = { columns: ['_source'], dataView: dataViewMock, - rows: ( - rows || [ - { - _index: 'mock_index', - _id: '1', - _score: 1, - fields: [ - { - timestamp: '2020-20-01T12:12:12.123', - }, - ], - _source: { message: 'mock_message', bytes: 20 }, - } as EsHitRecord, - ] - ).map((row) => buildDataTableRecord(row, dataViewMock)), - + rows: [ + { + _index: 'mock_index', + _id: '1', + _score: 1, + fields: [ + { + timestamp: '2020-20-01T12:12:12.123', + }, + ], + _source: { message: 'mock_message', bytes: 20 }, + } as EsHitRecord, + ].map((row) => buildDataTableRecord(row, dataViewMock)), sort: [['order_date', 'desc']], isLoading: false, searchDescription: '', @@ -49,6 +47,7 @@ describe('Doc table component', () => { render: () => { return
    mock
    ; }, + ...(customProps || {}), }; return mountWithIntl( @@ -66,9 +65,16 @@ describe('Doc table component', () => { }); it('should render error fallback if rows array is empty', () => { - const component = mountComponent([]); + const component = mountComponent({ rows: [], isLoading: false }); + expect(findTestSubject(component, 'discoverDocTable').exists()).toBeTruthy(); + expect(findTestSubject(component, 'docTable').exists()).toBeFalsy(); + expect(component.find('.kbnDocTable__error').find(EuiIcon).exists()).toBeTruthy(); + }); + + it('should render loading indicator', () => { + const component = mountComponent({ rows: [], isLoading: true }); expect(findTestSubject(component, 'discoverDocTable').exists()).toBeTruthy(); expect(findTestSubject(component, 'docTable').exists()).toBeFalsy(); - expect(component.find('.kbnDocTable__error').exists()).toBeTruthy(); + expect(component.find('.kbnDocTable__error').find(EuiLoadingSpinner).exists()).toBeTruthy(); }); }); 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 bf940c79be545..9d4d40242ab25 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 @@ -7,7 +7,7 @@ */ import React, { forwardRef, useCallback, useMemo } from 'react'; -import { EuiIcon, EuiSpacer, EuiText } from '@elastic/eui'; +import { EuiIcon, EuiLoadingSpinner, EuiSpacer, EuiText } from '@elastic/eui'; import type { DataView, DataViewField } from '@kbn/data-views-plugin/public'; import type { SortOrder } from '@kbn/saved-search-plugin/public'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -218,12 +218,22 @@ export const DocTableWrapper = forwardRef( {!rows.length && (
    - - - + {isLoading ? ( + <> + + + + + ) : ( + <> + + + + + )}
    )} diff --git a/src/plugins/discover/public/hooks/use_data_grid_columns.ts b/src/plugins/discover/public/hooks/use_data_grid_columns.ts index c693117916d71..22fc8e9836888 100644 --- a/src/plugins/discover/public/hooks/use_data_grid_columns.ts +++ b/src/plugins/discover/public/hooks/use_data_grid_columns.ts @@ -11,7 +11,7 @@ import type { DataView, DataViewsContract } from '@kbn/data-views-plugin/public' import { Capabilities, IUiSettingsClient } from '@kbn/core/public'; import { isEqual } from 'lodash'; -import { DiscoverStateContainer as DiscoverGetStateReturn } from '../application/main/services/discover_state'; +import { DiscoverAppStateContainer } from '../application/main/services/discover_app_state_container'; import { GetStateReturn as ContextGetStateReturn } from '../application/context/services/context_state'; import { getStateColumnActions } from '../components/doc_table/actions/columns'; @@ -21,7 +21,7 @@ interface UseColumnsProps { dataView: DataView; dataViews: DataViewsContract; useNewFieldsApi: boolean; - setAppState: DiscoverGetStateReturn['setAppState'] | ContextGetStateReturn['setAppState']; + setAppState: DiscoverAppStateContainer['update'] | ContextGetStateReturn['setAppState']; columns?: string[]; sort?: string[][]; } diff --git a/src/plugins/files/common/default_image_file_kind.ts b/src/plugins/files/common/default_image_file_kind.ts index 2fd2812e6f17b..9e20f2f058747 100644 --- a/src/plugins/files/common/default_image_file_kind.ts +++ b/src/plugins/files/common/default_image_file_kind.ts @@ -6,30 +6,14 @@ * Side Public License, v 1. */ -import { FileKind } from './types'; +import { FileKindBase } from '@kbn/shared-ux-file-types'; -const id = 'defaultImage' as const; -const tag = 'files:defaultImage' as const; -const tags = [`access:${tag}`]; -const tenMebiBytes = 1024 * 1024 * 10; +export const id = 'defaultImage' as const; +export const tag = 'files:defaultImage' as const; +export const tags = [`access:${tag}`]; +export const maxSize = 1024 * 1024 * 10; -/** - * A file kind that is available to all plugins to use for uploading images - * intended to be reused across Kibana. - */ -export const defaultImageFileKind: FileKind = { +export const kind: FileKindBase = { id, - maxSizeBytes: tenMebiBytes, - blobStoreSettings: {}, - // tried using "image/*" but it did not work with the HTTP endpoint (got 415 Unsupported Media Type) allowedMimeTypes: ['image/png', 'image/jpeg', 'image/webp', 'image/avif'], - http: { - create: { tags }, - delete: { tags }, - download: { tags }, - getById: { tags }, - list: { tags }, - share: { tags }, - update: { tags }, - }, }; diff --git a/src/plugins/files/common/file_kinds_registry/index.ts b/src/plugins/files/common/file_kinds_registry/index.ts index afed906bd0aa3..58e2f51bca6a0 100644 --- a/src/plugins/files/common/file_kinds_registry/index.ts +++ b/src/plugins/files/common/file_kinds_registry/index.ts @@ -6,36 +6,39 @@ * Side Public License, v 1. */ -import { createGetterSetter } from '@kbn/kibana-utils-plugin/common'; import assert from 'assert'; -import { FileKind } from '..'; +import { createGetterSetter } from '@kbn/kibana-utils-plugin/common'; +import type { FileKindBase } from '@kbn/shared-ux-file-types'; +import type { FileKind } from '../types'; -export interface FileKindsRegistry { +export interface FileKindsRegistry { /** * Register a new file kind. */ - register(fileKind: FileKind): void; + register(fileKind: FK): void; /** * Gets a {@link FileKind} or throws. */ - get(id: string): FileKind; + get(id: string): FK; /** * Return all registered {@link FileKind}s. */ - getAll(): FileKind[]; + getAll(): FK[]; } /** * @internal */ -export class FileKindsRegistryImpl implements FileKindsRegistry { - constructor(private readonly onRegister?: (fileKind: FileKind) => void) {} +export class FileKindsRegistryImpl + implements FileKindsRegistry +{ + constructor(private readonly onRegister?: (fileKind: FK) => void) {} - private readonly fileKinds = new Map(); + private readonly fileKinds = new Map(); - register(fileKind: FileKind) { + register(fileKind: FK) { if (this.fileKinds.get(fileKind.id)) { throw new Error(`File kind "${fileKind.id}" already registered.`); } @@ -50,13 +53,13 @@ export class FileKindsRegistryImpl implements FileKindsRegistry { this.onRegister?.(fileKind); } - get(id: string): FileKind { + get(id: string): FK { const fileKind = this.fileKinds.get(id); assert(fileKind, `File kind with id "${id}" not found.`); return fileKind; } - getAll(): FileKind[] { + getAll(): FK[] { return Array.from(this.fileKinds.values()); } } diff --git a/src/plugins/files/common/index.ts b/src/plugins/files/common/index.ts index 05d32a0645a0d..96319e9651dc2 100755 --- a/src/plugins/files/common/index.ts +++ b/src/plugins/files/common/index.ts @@ -7,11 +7,11 @@ */ export { FILE_SO_TYPE, PLUGIN_ID, PLUGIN_NAME, ES_FIXED_SIZE_INDEX_BLOB_STORE } from './constants'; -export { defaultImageFileKind } from './default_image_file_kind'; export type { File, FileKind, + FileKindBrowser, FileJSON, FileShare, FileStatus, @@ -29,3 +29,6 @@ export type { FileShareJSONWithToken, UpdatableFileShareMetadata, } from './types'; + +import * as DefaultFileKind from './default_image_file_kind'; +export { DefaultFileKind }; diff --git a/src/plugins/files/common/types.ts b/src/plugins/files/common/types.ts index b0f9b7e0b9b6f..c0cf5dacf687a 100644 --- a/src/plugins/files/common/types.ts +++ b/src/plugins/files/common/types.ts @@ -9,11 +9,20 @@ import type { SavedObject } from '@kbn/core/server'; import type { Observable } from 'rxjs'; import type { Readable } from 'stream'; -import type { FileJSON, FileStatus, FileMetadata } from '@kbn/shared-ux-file-types'; +import type { + FileJSON, + FileStatus, + FileMetadata, + FileShare, + FileShareJSON, + FileKindBase, + FileShareJSONWithToken, +} from '@kbn/shared-ux-file-types'; import type { ES_FIXED_SIZE_INDEX_BLOB_STORE } from './constants'; export type { - FileKind, + FileKindBase, + FileKindBrowser, FileJSON, FileStatus, FileMetadata, @@ -23,96 +32,107 @@ export type { FileImageMetadata, } from '@kbn/shared-ux-file-types'; -/** - * Values for paginating through results. - */ -export interface Pagination { - /** - * Page of results. - */ - page?: number; - /** - * Number of results per page. - */ - perPage?: number; -} - -/** - * An {@link SavedObject} containing a file object (i.e., metadata only). - */ -export type FileSavedObject = SavedObject>; - -/** - * The set of file metadata that can be updated. - */ -export type UpdatableFileMetadata = Pick, 'meta' | 'alt' | 'name'>; - -/** - * Data stored with a file share object +/* + * A descriptor of meta values associated with a set or "kind" of files. + * + * @note In order to use the file service consumers must register a {@link FileKind} + * in the {@link FileKindsRegistry}. */ -// eslint-disable-next-line @typescript-eslint/consistent-type-definitions -export type FileShare = { +export interface FileKind extends FileKindBase { /** - * ISO timestamp of when the file share was created. + * Max file contents size, in bytes. Can be customized per file using the + * {@link FileJSON} object. This is enforced on the server-side as well as + * in the upload React component. + * + * @default 4MiB */ - created: string; + maxSizeBytes?: number | ((file: FileJSON) => number); /** - * Secret token used to access the associated file. + * Blob store specific settings that enable configuration of storage + * details. */ - token: string; + blobStoreSettings?: BlobStorageSettings; /** - * Human friendly name for this share token. + * Specify which HTTP routes to create for the file kind. + * + * You can always create your own HTTP routes for working with files but + * this interface allows you to expose basic CRUD operations, upload, download + * and sharing of files over a RESTful-like interface. + * + * @note The public {@link FileClient} uses these endpoints. */ - name?: string; + http: { + /** + * Expose file creation (and upload) over HTTP. + */ + create?: HttpEndpointDefinition; + /** + * Expose file updates over HTTP. + */ + update?: HttpEndpointDefinition; + /** + * Expose file deletion over HTTP. + */ + delete?: HttpEndpointDefinition; + /** + * Expose "get by ID" functionality over HTTP. + */ + getById?: HttpEndpointDefinition; + /** + * Expose the ability to list all files of this kind over HTTP. + */ + list?: HttpEndpointDefinition; + /** + * Expose the ability to download a file's contents over HTTP. + */ + download?: HttpEndpointDefinition; + /** + * Expose file share functionality over HTTP. + */ + share?: HttpEndpointDefinition; + }; +} +/** Definition for an endpoint that the File's service will generate */ +interface HttpEndpointDefinition { /** - * The unix timestamp (in milliseconds) this file share will expire. + * Specify the tags for this endpoint. + * + * @example + * // This will enable access control to this endpoint for users that can access "myApp" only. + * { tags: ['access:myApp'] } * - * TODO: in future we could add a special value like "forever", but this should - * not be the default. */ - valid_until: number; -}; + tags: string[]; +} /** - * Attributes of a file that represent a serialised version of the file. + * Values for paginating through results. */ -export interface FileShareJSON { - /** - * Unique ID share instance - */ - id: string; - /** - * ISO timestamp the share was created - */ - created: FileShare['created']; - /** - * Unix timestamp (in milliseconds) of when this share expires - */ - validUntil: FileShare['valid_until']; +export interface Pagination { /** - * A user-friendly name for the file share + * Page of results. */ - name?: FileShare['name']; + page?: number; /** - * The ID of the file this share is linked to + * Number of results per page. */ - fileId: string; + perPage?: number; } /** - * A version of the file share with a token included. - * - * @note This should only be shown when the file share is first created + * An {@link SavedObject} containing a file object (i.e., metadata only). */ -export type FileShareJSONWithToken = FileShareJSON & { - /** - * Secret token that can be used to access files - */ - token: string; -}; +export type FileSavedObject = SavedObject>; + +/** + * The set of file metadata that can be updated. + */ +export type UpdatableFileMetadata = Pick, 'meta' | 'alt' | 'name'>; + +export type { FileShare, FileShareJSON, FileShareJSONWithToken }; /** * Set of attributes that can be updated in a file share. diff --git a/src/plugins/files/public/files_client/files_client.ts b/src/plugins/files/public/files_client/files_client.ts index 9044ab58cbf7e..f091f3317b4b1 100644 --- a/src/plugins/files/public/files_client/files_client.ts +++ b/src/plugins/files/public/files_client/files_client.ts @@ -7,8 +7,9 @@ */ import type { HttpStart } from '@kbn/core/public'; +import type { FileKindBrowser } from '@kbn/shared-ux-file-types'; import type { ScopedFilesClient, FilesClient } from '../types'; -import { getFileKindsRegistry } from '../../common/file_kinds_registry'; +import { FileKindsRegistryImpl } from '../../common/file_kinds_registry'; import { API_BASE_PATH, FILES_API_BASE_PATH, @@ -56,6 +57,11 @@ export const apiRoutes = { * Arguments to create a new {@link FileClient}. */ export interface Args { + /** + * Registry of file kinds. + */ + registry: FileKindsRegistryImpl; + /** * The http start service from core. */ @@ -81,9 +87,11 @@ const commonBodyHeaders = { export function createFilesClient(args: Args): FilesClient; export function createFilesClient(scopedArgs: ScopedArgs): ScopedFilesClient; export function createFilesClient({ + registry, http, fileKind: scopedFileKind, }: { + registry: FileKindsRegistryImpl; http: HttpStart; fileKind?: string; }): FilesClient | ScopedFilesClient { @@ -172,7 +180,7 @@ export function createFilesClient({ return http.get(apiRoutes.getPublicDownloadRoute(fileName), { query: { token } }); }, getFileKind(id: string) { - return getFileKindsRegistry().get(id); + return registry.get(id); }, }; return api; diff --git a/src/plugins/files/public/index.ts b/src/plugins/files/public/index.ts index 168780bd92380..d4970fe4f3bff 100644 --- a/src/plugins/files/public/index.ts +++ b/src/plugins/files/public/index.ts @@ -7,7 +7,6 @@ */ import { FilesPlugin } from './plugin'; -export { defaultImageFileKind } from '../common/default_image_file_kind'; export type { FilesSetup, FilesStart } from './plugin'; export type { FilesClient, diff --git a/src/plugins/files/public/plugin.ts b/src/plugins/files/public/plugin.ts index 57f17fd6a3753..54646e9199f9a 100644 --- a/src/plugins/files/public/plugin.ts +++ b/src/plugins/files/public/plugin.ts @@ -7,16 +7,12 @@ */ import type { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; -import { - getFileKindsRegistry, - setFileKindsRegistry, - FileKindsRegistryImpl, -} from '../common/file_kinds_registry'; import type { FilesClient, FilesClientFactory } from './types'; +import { FileKindsRegistryImpl } from '../common/file_kinds_registry'; import { createFilesClient } from './files_client'; -import { FileKind } from '../common'; -import { registerDefaultFileKinds } from '../common/register_default_file_kinds'; +import { FileKindBrowser } from '../common'; import { ScopedFilesClient } from '.'; +import * as DefaultImageFileKind from '../common/default_image_file_kind'; /** * Public setup-phase contract @@ -24,7 +20,7 @@ import { ScopedFilesClient } from '.'; export interface FilesSetup { /** * A factory for creating an {@link FilesClient} instance. This requires a - * registered {@link FileKind}. + * registered {@link FileKindBrowser}. * * @track-adoption */ @@ -36,7 +32,7 @@ export interface FilesSetup { * * @param {FileKind} fileKind - the file kind to register */ - registerFileKind(fileKind: FileKind): void; + registerFileKind(fileKind: FileKindBrowser): void; } export type FilesStart = Pick; @@ -45,26 +41,35 @@ export type FilesStart = Pick; * Bringing files to Kibana */ export class FilesPlugin implements Plugin { - private filesClientFactory: undefined | FilesClientFactory; - - constructor() { - setFileKindsRegistry(new FileKindsRegistryImpl()); - } + private registry = new FileKindsRegistryImpl(); + private filesClientFactory?: FilesClientFactory; setup(core: CoreSetup): FilesSetup { + this.registry.register({ + ...DefaultImageFileKind.kind, + maxSizeBytes: DefaultImageFileKind.maxSize, + }); + this.filesClientFactory = { - asScoped(fileKind: string) { - return createFilesClient({ fileKind, http: core.http }) as ScopedFilesClient; + asScoped: (fileKind: string) => { + return createFilesClient({ + registry: this.registry, + fileKind, + http: core.http, + }) as ScopedFilesClient; }, - asUnscoped() { - return createFilesClient({ http: core.http }) as FilesClient; + asUnscoped: () => { + return createFilesClient({ + registry: this.registry, + http: core.http, + }) as FilesClient; }, }; - registerDefaultFileKinds(); + return { filesClientFactory: this.filesClientFactory, - registerFileKind: (fileKind: FileKind) => { - getFileKindsRegistry().register(fileKind); + registerFileKind: (fileKind: FileKindBrowser) => { + this.registry.register(fileKind); }, }; } diff --git a/src/plugins/files/server/file/file.ts b/src/plugins/files/server/file/file.ts index d8f246d864882..fd5a27fd1bfaa 100644 --- a/src/plugins/files/server/file/file.ts +++ b/src/plugins/files/server/file/file.ts @@ -71,7 +71,7 @@ export class File implements IFile { } private upload(content: Readable): Observable<{ size: number }> { - return defer(() => this.fileClient.upload(this.id, content)); + return defer(() => this.fileClient.upload(this.metadata, content)); } public async uploadContent( diff --git a/src/plugins/files/server/file_client/create_es_file_client.ts b/src/plugins/files/server/file_client/create_es_file_client.ts index 1fb14b1fdfbba..90c5ef8893b14 100644 --- a/src/plugins/files/server/file_client/create_es_file_client.ts +++ b/src/plugins/files/server/file_client/create_es_file_client.ts @@ -27,15 +27,15 @@ export interface CreateEsFileClientArgs { */ blobStorageIndex: string; /** - * An elasticsearch client that will be used to interact with the cluster + * An elasticsearch client that will be used to interact with the cluster. */ elasticsearchClient: ElasticsearchClient; /** - * The maximum file size to be write + * The maximum file size to be written. */ maxSizeBytes?: number; /** - * A logger for debuggin purposes + * A logger for debugging purposes. */ logger: Logger; } diff --git a/src/plugins/files/server/file_client/file_client.ts b/src/plugins/files/server/file_client/file_client.ts index 8e1a8d2fdb933..f08648aac107b 100644 --- a/src/plugins/files/server/file_client/file_client.ts +++ b/src/plugins/files/server/file_client/file_client.ts @@ -43,6 +43,8 @@ import { export type UploadOptions = Omit; +const fourMiB = 4 * 1024 * 1024; + export function createFileClient({ fileKindDescriptor, auditLogger, @@ -211,17 +213,28 @@ export class FileClientImpl implements FileClient { * @param options - Options for the upload */ public upload = async ( - id: string, + file: FileJSON, rs: Readable, options?: UploadOptions ): ReturnType => { + const { maxSizeBytes } = this.fileKindDescriptor; + const { transforms = [], ...blobOptions } = options || {}; + + let maxFileSize: number = typeof maxSizeBytes === 'number' ? maxSizeBytes : fourMiB; + + if (typeof maxSizeBytes === 'function') { + const sizeLimitPerFile = maxSizeBytes(file); + if (typeof sizeLimitPerFile === 'number') { + maxFileSize = sizeLimitPerFile; + } + } + + transforms.push(enforceMaxByteSizeTransform(maxFileSize)); + return this.blobStorageClient.upload(rs, { - ...options, - transforms: [ - ...(options?.transforms || []), - enforceMaxByteSizeTransform(this.fileKindDescriptor.maxSizeBytes ?? Infinity), - ], - id, + ...blobOptions, + transforms, + id: file.id, }); }; diff --git a/src/plugins/files/server/integration_tests/file_service.test.ts b/src/plugins/files/server/integration_tests/file_service.test.ts index 10aa11290a3e9..25d7f463de03a 100644 --- a/src/plugins/files/server/integration_tests/file_service.test.ts +++ b/src/plugins/files/server/integration_tests/file_service.test.ts @@ -63,7 +63,9 @@ describe('FileService', () => { }); fileKindsRegistry.register({ id: fileKindTinyFiles, - maxSizeBytes: 10, + maxSizeBytes: (file) => { + return file.mimeType === 'text/json' ? 3 : 10; + }, http: {}, }); esClient = coreStart.elasticsearch.client.asInternalUser; @@ -263,7 +265,7 @@ describe('FileService', () => { expect(updatedFile2.data.alt).toBe(updatableFields.alt); }); - it('enforces max size settings', async () => { + it('enforces file kind max size settings', async () => { const file = await createDisposableFile({ fileKind: fileKindTinyFiles, name: 'test' }); const tinyContent = Readable.from(['ok']); await file.uploadContent(tinyContent); @@ -275,6 +277,26 @@ describe('FileService', () => { ); }); + it('enforces per file max size settings, using mime type', async () => { + const file = await createDisposableFile({ + fileKind: fileKindTinyFiles, + name: 'test', + mime: 'text/mime', + }); + const tinyContent = Readable.from(['ok ok ok']); + await file.uploadContent(tinyContent); + + const file2 = await createDisposableFile({ + fileKind: fileKindTinyFiles, + name: 'test', + mime: 'text/json', + }); + const notSoTinyContent = Readable.from(['[123]']); + await expect(() => file2.uploadContent(notSoTinyContent)).rejects.toThrow( + new Error('Maximum of 3 bytes exceeded') + ); + }); + describe('ES blob integration and file kinds', () => { it('passes blob store settings', async () => { const file = await createDisposableFile({ fileKind: fileKindNonDefault, name: 'test' }); diff --git a/src/plugins/files/server/plugin.ts b/src/plugins/files/server/plugin.ts index 1680512ded3d2..ba63e08b2ed21 100755 --- a/src/plugins/files/server/plugin.ts +++ b/src/plugins/files/server/plugin.ts @@ -21,7 +21,6 @@ import { getFileKindsRegistry, FileKindsRegistryImpl, } from '../common/file_kinds_registry'; -import { registerDefaultFileKinds } from '../common/register_default_file_kinds'; import { BlobStorageService } from './blob_storage_service'; import { FileServiceFactory } from './file_service'; @@ -35,6 +34,7 @@ import type { import type { FilesRequestHandlerContext, FilesRouter } from './routes/types'; import { registerRoutes, registerFileKindRoutes } from './routes'; import { Counters, registerUsageCollector } from './usage'; +import * as DefaultImageKind from '../common/default_image_file_kind'; export class FilesPlugin implements Plugin { private static analytics?: AnalyticsServiceStart; @@ -92,8 +92,7 @@ export class FilesPlugin implements Plugin this.fileServiceFactory?.asInternal(), }); - // Now that everything is set up: - registerDefaultFileKinds(); + this.registerDefaultImageFileKind(); return { registerFileKind(fileKind) { @@ -125,4 +124,21 @@ export class FilesPlugin implements Plugin = async ({ files, fileKind }, req, try { await file.uploadContent(stream as Readable, abort$); } catch (e) { + if (e instanceof MaxByteSizeExceededError) { + return res.customError({ + statusCode: 413, + }); + } + if ( e instanceof fileErrors.ContentAlreadyUploadedError || e instanceof fileErrors.UploadInProgressError @@ -81,8 +88,6 @@ export const handler: CreateHandler = async ({ files, fileKind }, req, return res.ok({ body }); }; -const fourMiB = 4 * 1024 * 1024; - export function register(fileKindRouter: FileKindRouter, fileKind: FileKind) { if (fileKind.http.create) { fileKindRouter[method]( @@ -97,7 +102,12 @@ export function register(fileKindRouter: FileKindRouter, fileKind: FileKind) { output: 'stream', parse: false, accepts: fileKind.allowedMimeTypes ?? 'application/octet-stream', - maxBytes: fileKind.maxSizeBytes ?? fourMiB, + + // This is set to 10 GiB because the actual file size limit is + // enforced by the file service. This is just a limit on the + // size of the HTTP request body, but the file service will throw + // 413 errors if the file size is larger than expected. + maxBytes: 10 * 1024 * 1024 * 1024, }, }, }, diff --git a/src/plugins/files/server/usage/integration_tests/usage.test.ts b/src/plugins/files/server/usage/integration_tests/usage.test.ts index 0bccb869c822e..02cc7dfee55fa 100644 --- a/src/plugins/files/server/usage/integration_tests/usage.test.ts +++ b/src/plugins/files/server/usage/integration_tests/usage.test.ts @@ -44,9 +44,11 @@ describe('Files usage telemetry', () => { request.post(root, `/api/files/shares/${fileKind}/${file3.id}`).send({}).expect(200), ]); - const { body } = await request.get(root, `/api/stats?extended=true&legacy=true`); + const { body } = await request + .post(root, '/api/telemetry/v2/clusters/_stats') + .send({ unencrypted: true }); - expect(body.usage.files).toMatchInlineSnapshot(` + expect(body[0].stats.stack_stats.kibana.plugins.files).toMatchInlineSnapshot(` Object { "countByExtension": Array [ Object { diff --git a/src/plugins/image_embeddable/public/imports.ts b/src/plugins/image_embeddable/public/imports.ts index 59acef5e42633..923ff8bf39b22 100644 --- a/src/plugins/image_embeddable/public/imports.ts +++ b/src/plugins/image_embeddable/public/imports.ts @@ -6,8 +6,7 @@ * Side Public License, v 1. */ -import { FileKind } from '@kbn/shared-ux-file-types'; -import { defaultImageFileKind } from '@kbn/files-plugin/common'; +import { DefaultFileKind } from '@kbn/files-plugin/common'; export type { FilesClient, @@ -27,4 +26,4 @@ export type { ApplicationStart, OverlayStart, ThemeServiceStart } from '@kbn/cor export type { UiActionsStart, UiActionsSetup } from '@kbn/ui-actions-plugin/public'; -export const imageEmbeddableFileKind: FileKind = defaultImageFileKind; +export const imageEmbeddableFileKind = DefaultFileKind.kind; diff --git a/src/plugins/kibana_react/public/index.ts b/src/plugins/kibana_react/public/index.ts index 816f3a7635a12..172f3227d479a 100644 --- a/src/plugins/kibana_react/public/index.ts +++ b/src/plugins/kibana_react/public/index.ts @@ -47,6 +47,7 @@ export { export { useExecutionContext } from './use_execution_context'; export type { ToolbarButtonProps } from './toolbar_button'; +/** @deprecated ToolbarButton - use `ToolbarButton` from `@kbn/shared-ux-button-toolbar` */ export { POSITIONS, WEIGHTS, TOOLBAR_BUTTON_SIZES, ToolbarButton } from './toolbar_button'; export { Route } from './router'; diff --git a/src/plugins/kibana_react/public/notifications/create_notifications.test.tsx b/src/plugins/kibana_react/public/notifications/create_notifications.test.tsx index 0b2c0a0478d53..0876472d89f1b 100644 --- a/src/plugins/kibana_react/public/notifications/create_notifications.test.tsx +++ b/src/plugins/kibana_react/public/notifications/create_notifications.test.tsx @@ -162,7 +162,7 @@ test('can display success, warning and danger toasts', () => { expect(notifications.toasts.add.mock.calls[2][0]).toMatchInlineSnapshot(` Object { "color": "danger", - "iconType": "alert", + "iconType": "error", "onClose": undefined, "text": MountPoint { "reactNode": , diff --git a/src/plugins/kibana_react/public/notifications/create_notifications.tsx b/src/plugins/kibana_react/public/notifications/create_notifications.tsx index 8eb16a5580ab3..ab2cccf7655a3 100644 --- a/src/plugins/kibana_react/public/notifications/create_notifications.tsx +++ b/src/plugins/kibana_react/public/notifications/create_notifications.tsx @@ -40,7 +40,7 @@ export const createNotifications = (services: KibanaServices): KibanaReactNotifi show({ color: 'warning', iconType: 'help', ...input }); const danger: KibanaReactNotifications['toasts']['danger'] = (input) => - show({ color: 'danger', iconType: 'alert', ...input }); + show({ color: 'danger', iconType: 'error', ...input }); const notifications: KibanaReactNotifications = { toasts: { diff --git a/src/plugins/presentation_util/public/components/field_picker/field_picker.scss b/src/plugins/presentation_util/public/components/field_picker/field_picker.scss index a06c469e713bf..bbb3e8eb44be3 100644 --- a/src/plugins/presentation_util/public/components/field_picker/field_picker.scss +++ b/src/plugins/presentation_util/public/components/field_picker/field_picker.scss @@ -1,17 +1,4 @@ -.presFieldPicker__fieldButton { - background: $euiColorEmptyShade; -} .presFieldPickerFieldButtonActive { - box-shadow: 0 0 0 2px $euiColorPrimary; -} - -.presFieldPicker__fieldPanel { - height: 300px; - overflow-y: scroll; -} - -.presFieldPicker__container--disabled { - opacity: .7; - pointer-events: none; + background-color: transparentize($euiColorPrimary, .9); } \ No newline at end of file diff --git a/src/plugins/presentation_util/public/components/field_picker/field_picker.tsx b/src/plugins/presentation_util/public/components/field_picker/field_picker.tsx index 3c8a084f2686b..3d6a42d7211a8 100644 --- a/src/plugins/presentation_util/public/components/field_picker/field_picker.tsx +++ b/src/plugins/presentation_util/public/components/field_picker/field_picker.tsx @@ -8,13 +8,14 @@ import classNames from 'classnames'; import { sortBy, uniq } from 'lodash'; -import React, { useState } from 'react'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui'; -import { FieldButton, FieldIcon } from '@kbn/react-field'; +import React, { useEffect, useMemo, useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { FieldIcon } from '@kbn/react-field'; +import { EuiSelectable, EuiSelectableOption, EuiSpacer } from '@elastic/eui'; import { DataView, DataViewField } from '@kbn/data-views-plugin/common'; -import { FieldSearch } from './field_search'; + +import { FieldTypeFilter } from './field_type_filter'; import './field_picker.scss'; @@ -31,115 +32,102 @@ export const FieldPicker = ({ filterPredicate, selectedFieldName, }: FieldPickerProps) => { - const [nameFilter, setNameFilter] = useState(''); const [typesFilter, setTypesFilter] = useState([]); + const [fieldSelectableOptions, setFieldSelectableOptions] = useState([]); - // Retrieve, filter, and sort fields from data view - const fields = dataView - ? sortBy( - dataView.fields - .filter( - (f) => - f.name.toLowerCase().includes(nameFilter.toLowerCase()) && - (typesFilter.length === 0 || typesFilter.includes(f.type as string)) - ) + const availableFields = useMemo( + () => + sortBy( + (dataView?.fields ?? []) + .filter((f) => typesFilter.length === 0 || typesFilter.includes(f.type as string)) .filter((f) => (filterPredicate ? filterPredicate(f) : true)), ['name'] - ) - : []; + ), + [dataView, filterPredicate, typesFilter] + ); + + useEffect(() => { + if (!dataView) return; + const options: EuiSelectableOption[] = (availableFields ?? []).map((field) => { + return { + key: field.name, + label: field.displayName ?? field.name, + className: classNames('presFieldPicker__fieldButton', { + presFieldPickerFieldButtonActive: field.name === selectedFieldName, + }), + 'data-test-subj': `field-picker-select-${field.name}`, + prepend: ( + + ), + }; + }); + setFieldSelectableOptions(options); + }, [availableFields, dataView, filterPredicate, selectedFieldName, typesFilter]); - const uniqueTypes = dataView - ? uniq( - dataView.fields - .filter((f) => (filterPredicate ? filterPredicate(f) : true)) - .map((f) => f.type as string) - ) - : []; + const uniqueTypes = useMemo( + () => + dataView + ? uniq( + dataView.fields + .filter((f) => (filterPredicate ? filterPredicate(f) : true)) + .map((f) => f.type as string) + ) + : [], + [dataView, filterPredicate] + ); + + const fieldTypeFilter = ( + setTypesFilter(types)} + fieldTypesValue={typesFilter} + availableFieldTypes={uniqueTypes} + /> + ); return ( - { + setFieldSelectableOptions(options); + if (!dataView || !changedOption.key) return; + const field = dataView.getFieldByName(changedOption.key); + if (field) onSelectField?.(field); + }} + searchProps={{ + 'data-test-subj': 'field-search-input', + placeholder: i18n.translate('presentationUtil.fieldSearch.searchPlaceHolder', { + defaultMessage: 'Search field names', + }), + }} + listProps={{ + isVirtualized: true, + showIcons: false, + bordered: true, + }} + height={300} > - - setNameFilter(val)} - searchValue={nameFilter} - onFieldTypesChange={(types) => setTypesFilter(types)} - fieldTypesValue={typesFilter} - availableFieldTypes={uniqueTypes} - /> - - - - {fields.length > 0 && ( - - {fields.map((f, i) => { - return ( - - { - onSelectField?.(f); - }} - isActive={f.name === selectedFieldName} - fieldName={f.name} - fieldIcon={} - /> - - ); - })} - - )} - {!dataView && ( - - - - - - - - )} - {dataView && fields.length === 0 && ( - - - - - - - - )} - - - + {(list, search) => ( + <> + {search} + + {fieldTypeFilter} + + {list} + + )} + ); }; diff --git a/src/plugins/presentation_util/public/components/field_picker/field_search.tsx b/src/plugins/presentation_util/public/components/field_picker/field_search.tsx deleted file mode 100644 index d3307f71988f1..0000000000000 --- a/src/plugins/presentation_util/public/components/field_picker/field_search.tsx +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 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, { useState } from 'react'; -import { i18n } from '@kbn/i18n'; -import { - EuiFieldSearch, - EuiFilterGroup, - EuiFlexGroup, - EuiFlexItem, - EuiPopover, - EuiContextMenuPanel, - EuiContextMenuItem, - EuiOutsideClickDetector, - EuiFilterButton, - EuiSpacer, - EuiPopoverTitle, -} from '@elastic/eui'; -import { FieldIcon } from '@kbn/react-field'; -import { FormattedMessage } from '@kbn/i18n-react'; - -import './field_search.scss'; - -export interface Props { - onSearchChange: (value: string) => void; - searchValue?: string; - - onFieldTypesChange: (value: string[]) => void; - fieldTypesValue: string[]; - - availableFieldTypes: string[]; -} - -export function FieldSearch({ - onSearchChange, - searchValue, - onFieldTypesChange, - fieldTypesValue, - availableFieldTypes, -}: Props) { - const searchPlaceholder = i18n.translate('presentationUtil.fieldSearch.searchPlaceHolder', { - defaultMessage: 'Search field names', - }); - - const [isPopoverOpen, setPopoverOpen] = useState(false); - - const handleFilterButtonClicked = () => { - setPopoverOpen(!isPopoverOpen); - }; - - const buttonContent = ( - 0} - numFilters={0} - hasActiveFilters={fieldTypesValue.length > 0} - numActiveFilters={fieldTypesValue.length} - onClick={handleFilterButtonClicked} - > - - - ); - - return ( - - - - onSearchChange(event.currentTarget.value)} - placeholder={searchPlaceholder} - value={searchValue} - /> - - - - {}} isDisabled={!isPopoverOpen}> - - { - setPopoverOpen(false); - }} - button={buttonContent} - > - - {i18n.translate('presentationUtil.fieldSearch.filterByTypeLabel', { - defaultMessage: 'Filter by type', - })} - - ( - { - if (fieldTypesValue.includes(type)) { - onFieldTypesChange(fieldTypesValue.filter((f) => f !== type)); - } else { - onFieldTypesChange([...fieldTypesValue, type]); - } - }} - > - - - - - {type} - - - ))} - /> - - - - - ); -} diff --git a/src/plugins/presentation_util/public/components/field_picker/field_search.scss b/src/plugins/presentation_util/public/components/field_picker/field_type_filter.scss similarity index 100% rename from src/plugins/presentation_util/public/components/field_picker/field_search.scss rename to src/plugins/presentation_util/public/components/field_picker/field_type_filter.scss diff --git a/src/plugins/presentation_util/public/components/field_picker/field_type_filter.tsx b/src/plugins/presentation_util/public/components/field_picker/field_type_filter.tsx new file mode 100644 index 0000000000000..bb27137ce16c8 --- /dev/null +++ b/src/plugins/presentation_util/public/components/field_picker/field_type_filter.tsx @@ -0,0 +1,107 @@ +/* + * Copyright 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, { useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiFilterGroup, + EuiFlexGroup, + EuiFlexItem, + EuiPopover, + EuiContextMenuPanel, + EuiContextMenuItem, + EuiOutsideClickDetector, + EuiFilterButton, + EuiPopoverTitle, +} from '@elastic/eui'; +import { FieldIcon } from '@kbn/react-field'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import './field_type_filter.scss'; + +export interface Props { + onFieldTypesChange: (value: string[]) => void; + fieldTypesValue: string[]; + + availableFieldTypes: string[]; +} + +export function FieldTypeFilter({ + onFieldTypesChange, + fieldTypesValue, + availableFieldTypes, +}: Props) { + const [isPopoverOpen, setPopoverOpen] = useState(false); + + const handleFilterButtonClicked = () => { + setPopoverOpen(!isPopoverOpen); + }; + + const buttonContent = ( + 0} + numFilters={0} + hasActiveFilters={fieldTypesValue.length > 0} + numActiveFilters={fieldTypesValue.length} + onClick={handleFilterButtonClicked} + > + + + ); + + return ( + {}} isDisabled={!isPopoverOpen}> + + { + setPopoverOpen(false); + }} + button={buttonContent} + > + + {i18n.translate('presentationUtil.fieldSearch.filterByTypeLabel', { + defaultMessage: 'Filter by type', + })} + + ( + { + if (fieldTypesValue.includes(type)) { + onFieldTypesChange(fieldTypesValue.filter((f) => f !== type)); + } else { + onFieldTypesChange([...fieldTypesValue, type]); + } + }} + > + + + + + {type} + + + ))} + /> + + + + ); +} diff --git a/src/plugins/presentation_util/public/components/solution_toolbar/index.ts b/src/plugins/presentation_util/public/components/solution_toolbar/index.ts deleted file mode 100644 index 84e313252d2e4..0000000000000 --- a/src/plugins/presentation_util/public/components/solution_toolbar/index.ts +++ /dev/null @@ -1,15 +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. - */ - -/** @deprecated SolutionToolbar - use `Toolbar` from @kbn/shared-ux-button-toolbar" */ -export { SolutionToolbar } from './solution_toolbar'; -/** @deprecated QuickButtonGroup - use `IconButtonGroup` from `@kbn/shared-ux-button-toolbar` */ -/** @deprecated AddFromLibraryButton - use `AddFromLibraryButton` from `@kbn/shared-ux-button-toolbar` */ -/** @deprecated PrimaryButton and Button - use `Primary` from `@kbn/shared-ux-button-toolbar` */ -/** @deprecated PrimaryActionPopover - use `ToolbarPopover` from @kbn/shared-ux-button-toolber` */ -export * from './items'; diff --git a/src/plugins/presentation_util/public/components/solution_toolbar/items/add_from_library.tsx b/src/plugins/presentation_util/public/components/solution_toolbar/items/add_from_library.tsx deleted file mode 100644 index 0550de1d069fa..0000000000000 --- a/src/plugins/presentation_util/public/components/solution_toolbar/items/add_from_library.tsx +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React from 'react'; -import { ComponentStrings } from '../../../i18n/components'; -import { SolutionToolbarButton, Props as SolutionToolbarButtonProps } from './button'; - -const { SolutionToolbar: strings } = ComponentStrings; - -export type Props = Omit; - -export const AddFromLibraryButton = ({ onClick, ...rest }: Props) => ( - -); diff --git a/src/plugins/presentation_util/public/components/solution_toolbar/items/button.scss b/src/plugins/presentation_util/public/components/solution_toolbar/items/button.scss deleted file mode 100644 index 4283813f1d0b7..0000000000000 --- a/src/plugins/presentation_util/public/components/solution_toolbar/items/button.scss +++ /dev/null @@ -1,12 +0,0 @@ -.solutionToolbarButton { - line-height: $euiButtonHeight; // Keeps alignment of text and chart icon - background-color: $euiColorEmptyShade; - - // Lighten the border color for all states - border-color: $euiBorderColor !important; // sass-lint:disable-line no-important - - &[class*='--text'] { - border-width: $euiBorderWidthThin; - border-style: solid; - } -} diff --git a/src/plugins/presentation_util/public/components/solution_toolbar/items/button.tsx b/src/plugins/presentation_util/public/components/solution_toolbar/items/button.tsx deleted file mode 100644 index a0a868875382d..0000000000000 --- a/src/plugins/presentation_util/public/components/solution_toolbar/items/button.tsx +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React from 'react'; -import { EuiButton } from '@elastic/eui'; -import { EuiButtonPropsForButton } from '@elastic/eui/src/components/button/button'; - -import './button.scss'; - -export interface Props - extends Pick { - label: string; - primary?: boolean; - isDarkModeEnabled?: boolean; -} - -export const SolutionToolbarButton = ({ label, primary, className, ...rest }: Props) => ( - - {label} - -); diff --git a/src/plugins/presentation_util/public/components/solution_toolbar/items/index.ts b/src/plugins/presentation_util/public/components/solution_toolbar/items/index.ts deleted file mode 100644 index cc6367bc3fcd5..0000000000000 --- a/src/plugins/presentation_util/public/components/solution_toolbar/items/index.ts +++ /dev/null @@ -1,16 +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. - */ - -export { SolutionToolbarButton } from './button'; -export { SolutionToolbarPopover } from './popover'; -export { AddFromLibraryButton } from './add_from_library'; -export type { QuickButtonProps } from './quick_group'; -/** @deprecated use `IconButtonGroup` from `@kbn/shared-ux-button-toolbar` */ -export { QuickButtonGroup } from './quick_group'; -export { PrimaryActionButton } from './primary_button'; -export { PrimaryActionPopover } from './primary_popover'; diff --git a/src/plugins/presentation_util/public/components/solution_toolbar/items/popover.tsx b/src/plugins/presentation_util/public/components/solution_toolbar/items/popover.tsx deleted file mode 100644 index 6f968d940b12d..0000000000000 --- a/src/plugins/presentation_util/public/components/solution_toolbar/items/popover.tsx +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React, { useState } from 'react'; -import { EuiPopover, EuiPopoverProps } from '@elastic/eui'; - -import { SolutionToolbarButton, Props as ButtonProps } from './button'; - -type AllowedButtonProps = Omit; -type AllowedPopoverProps = Omit< - EuiPopoverProps, - 'button' | 'isOpen' | 'closePopover' | 'anchorPosition' ->; - -export type Props = AllowedButtonProps & - AllowedPopoverProps & { - children: (arg: { closePopover: () => void }) => React.ReactNode; - }; - -export const SolutionToolbarPopover = ({ - label, - iconType, - primary, - iconSide, - children, - ...popover -}: Props) => { - const [isOpen, setIsOpen] = useState(false); - - const onButtonClick = () => setIsOpen((status) => !status); - const closePopover = () => setIsOpen(false); - - const button = ( - - ); - - return ( - - {children({ closePopover })} - - ); -}; diff --git a/src/plugins/presentation_util/public/components/solution_toolbar/items/primary_button.tsx b/src/plugins/presentation_util/public/components/solution_toolbar/items/primary_button.tsx deleted file mode 100644 index dcf16228ac63b..0000000000000 --- a/src/plugins/presentation_util/public/components/solution_toolbar/items/primary_button.tsx +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React from 'react'; - -import { SolutionToolbarButton, Props as SolutionToolbarButtonProps } from './button'; - -import './primary_button.scss'; - -export interface Props extends Omit { - isDarkModeEnabled?: boolean; -} - -export const PrimaryActionButton = ({ isDarkModeEnabled, ...props }: Props) => ( - -); diff --git a/src/plugins/presentation_util/public/components/solution_toolbar/items/quick_group.scss b/src/plugins/presentation_util/public/components/solution_toolbar/items/quick_group.scss deleted file mode 100644 index 9f70ae353405b..0000000000000 --- a/src/plugins/presentation_util/public/components/solution_toolbar/items/quick_group.scss +++ /dev/null @@ -1,25 +0,0 @@ -.quickButtonGroup { - .euiButtonGroup__buttons { - border-radius: $euiBorderRadius; - - .quickButtonGroup__button { - background-color: $euiColorEmptyShade; - // sass-lint:disable-block no-important - border-width: $euiBorderWidthThin !important; - border-style: solid !important; - border-color: $euiBorderColor !important; - } - - .quickButtonGroup__button:first-of-type { - // sass-lint:disable-block no-important - border-top-left-radius: $euiBorderRadius !important; - border-bottom-left-radius: $euiBorderRadius !important; - } - - .quickButtonGroup__button:last-of-type { - // sass-lint:disable-block no-important - border-top-right-radius: $euiBorderRadius !important; - border-bottom-right-radius: $euiBorderRadius !important; - } - } -} diff --git a/src/plugins/presentation_util/public/components/solution_toolbar/items/quick_group.tsx b/src/plugins/presentation_util/public/components/solution_toolbar/items/quick_group.tsx deleted file mode 100644 index 66b22eeb570db..0000000000000 --- a/src/plugins/presentation_util/public/components/solution_toolbar/items/quick_group.tsx +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React from 'react'; -import { EuiButtonGroup, htmlIdGenerator, EuiButtonGroupOptionProps } from '@elastic/eui'; -import { ComponentStrings } from '../../../i18n/components'; - -const { QuickButtonGroup: strings } = ComponentStrings; - -import './quick_group.scss'; - -export interface QuickButtonProps extends Pick { - createType: string; - onClick: () => void; -} - -export interface Props { - buttons: QuickButtonProps[]; -} - -type Option = EuiButtonGroupOptionProps & Omit; - -export const QuickButtonGroup = ({ buttons }: Props) => { - const buttonGroupOptions: Option[] = buttons.map((button: QuickButtonProps, index) => { - const { createType: label, ...rest } = button; - const title = strings.getAriaButtonLabel(label); - - return { - ...rest, - 'aria-label': title, - className: `quickButtonGroup__button`, - id: `${htmlIdGenerator()()}${index}`, - label, - title, - }; - }); - - const onChangeIconsMulti = (optionId: string) => { - buttonGroupOptions.find((x) => x.id === optionId)?.onClick(); - }; - - return ( - - ); -}; diff --git a/src/plugins/presentation_util/public/components/solution_toolbar/solution_toolbar.scss b/src/plugins/presentation_util/public/components/solution_toolbar/solution_toolbar.scss deleted file mode 100644 index 30ef3f1d2c44f..0000000000000 --- a/src/plugins/presentation_util/public/components/solution_toolbar/solution_toolbar.scss +++ /dev/null @@ -1,16 +0,0 @@ -.solutionToolbar { - flex-grow: 0; - - // Temporary fix for two tone icons to make them monochrome - .solutionToolbar--dark { - .euiIcon path { - fill: $euiColorInk; - } - } - - .solutionToolbar--light { - .euiIcon path { - fill: $euiColorGhost; - } - } -} diff --git a/src/plugins/presentation_util/public/components/solution_toolbar/solution_toolbar.stories.tsx b/src/plugins/presentation_util/public/components/solution_toolbar/solution_toolbar.stories.tsx deleted file mode 100644 index e9daaf4ad7912..0000000000000 --- a/src/plugins/presentation_util/public/components/solution_toolbar/solution_toolbar.stories.tsx +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React from 'react'; -import { Story } from '@storybook/react'; -import { action } from '@storybook/addon-actions'; -import { EuiContextMenu } from '@elastic/eui'; - -import { SolutionToolbar } from './solution_toolbar'; -import { SolutionToolbarPopover } from './items'; - -import { AddFromLibraryButton, PrimaryActionButton, QuickButtonGroup } from './items'; - -const quickButtons = [ - { - createType: 'Text', - onClick: action('onTextClick'), - iconType: 'visText', - }, - { - createType: 'Control', - onClick: action('onControlClick'), - iconType: 'controlsHorizontal', - }, - { - createType: 'Link', - onClick: action('onLinkClick'), - iconType: 'link', - }, - { - createType: 'Image', - onClick: action('onImageClick'), - iconType: 'image', - }, - { - createType: 'Markup', - onClick: action('onMarkupClick'), - iconType: 'visVega', - }, -]; - -const primaryButtonConfigs = { - Generic: ( - - ), - Canvas: ( - - {() => ( - - )} - - ), - Dashboard: ( - - ), -}; - -const extraButtonConfigs = { - Generic: undefined, - Canvas: undefined, - Dashboard: [ - - {() => ( - - )} - , - ], -}; - -export default { - title: 'Solution Toolbar', - description: 'A universal toolbar for solutions maintained by the Presentation Team.', - component: SolutionToolbar, - argTypes: { - quickButtonCount: { - defaultValue: 2, - control: { - type: 'number', - min: 0, - max: 5, - step: 1, - }, - }, - showAddFromLibraryButton: { - defaultValue: true, - control: { - type: 'boolean', - }, - }, - solution: { - table: { - disable: true, - }, - }, - }, - // https://github.com/storybookjs/storybook/issues/11543#issuecomment-684130442 - parameters: { - docs: { - source: { - type: 'code', - }, - }, - }, -}; - -const Template: Story<{ - solution: 'Generic' | 'Canvas' | 'Dashboard'; - quickButtonCount: number; - showAddFromLibraryButton: boolean; -}> = ({ quickButtonCount, solution, showAddFromLibraryButton }) => { - const primaryActionButton = primaryButtonConfigs[solution]; - const extraButtons = extraButtonConfigs[solution]; - let quickButtonGroup; - let addFromLibraryButton; - - if (quickButtonCount > 0) { - quickButtonGroup = ; - } - - if (showAddFromLibraryButton) { - addFromLibraryButton = ; - } - - return ( - - {{ - primaryActionButton, - quickButtonGroup, - extraButtons, - addFromLibraryButton, - }} - - ); -}; - -export const Generic = Template.bind({}); -Generic.args = { - ...Template.args, - solution: 'Generic', -}; - -export const Canvas = Template.bind({}); -Canvas.args = { - ...Template.args, - solution: 'Canvas', -}; - -export const Dashboard = Template.bind({}); -Dashboard.args = { - ...Template.args, - solution: 'Dashboard', -}; diff --git a/src/plugins/presentation_util/public/components/solution_toolbar/solution_toolbar.tsx b/src/plugins/presentation_util/public/components/solution_toolbar/solution_toolbar.tsx deleted file mode 100644 index 219c582f26a3a..0000000000000 --- a/src/plugins/presentation_util/public/components/solution_toolbar/solution_toolbar.tsx +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React, { ReactElement } from 'react'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; - -import { - QuickButtonGroup, - PrimaryActionButton, - SolutionToolbarButton, - PrimaryActionPopover, - SolutionToolbarPopover, -} from './items'; - -import './solution_toolbar.scss'; - -interface NamedSlots { - primaryActionButton: ReactElement; - quickButtonGroup?: ReactElement; - extraButtons?: Array< - ReactElement | undefined - >; -} - -export interface Props { - isDarkModeEnabled?: boolean; - children: NamedSlots; -} - -export const SolutionToolbar = ({ isDarkModeEnabled, children }: Props) => { - const { primaryActionButton, quickButtonGroup, extraButtons = [] } = children; - - const extra = extraButtons.map((button, index) => - button ? ( - - {button} - - ) : null - ); - - return ( - - {primaryActionButton} - - - {quickButtonGroup ? {quickButtonGroup} : null} - {extra} - - - - ); -}; diff --git a/src/plugins/presentation_util/public/i18n/components.ts b/src/plugins/presentation_util/public/i18n/components.ts deleted file mode 100644 index ab0e6d1bdbda0..0000000000000 --- a/src/plugins/presentation_util/public/i18n/components.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { i18n } from '@kbn/i18n'; - -export const ComponentStrings = { - SolutionToolbar: { - getEditorMenuButtonLabel: () => - i18n.translate('presentationUtil.solutionToolbar.editorMenuButtonLabel', { - defaultMessage: 'All editors', - }), - getLibraryButtonLabel: () => - i18n.translate('presentationUtil.solutionToolbar.libraryButtonLabel', { - defaultMessage: 'Add from library', - }), - }, - QuickButtonGroup: { - getAriaButtonLabel: (createType: string) => - i18n.translate('presentationUtil.solutionToolbar.quickButton.ariaButtonLabel', { - defaultMessage: `Create new {createType}`, - values: { - createType, - }, - }), - getLegend: () => - i18n.translate('presentationUtil.solutionToolbar.quickButton.legendLabel', { - defaultMessage: 'Quick create', - }), - }, -}; diff --git a/src/plugins/presentation_util/public/index.ts b/src/plugins/presentation_util/public/index.ts index 24bcc6af474de..51f8a2c902541 100644 --- a/src/plugins/presentation_util/public/index.ts +++ b/src/plugins/presentation_util/public/index.ts @@ -54,25 +54,6 @@ export type { OnExpressionInputEditorDidMount, } from './components/types'; -/** @deprecated QuickButtonProps - use `IconButtonGroupProps` from `@kbn/shared-ux-button-toolbar` */ -export type { QuickButtonProps } from './components/solution_toolbar'; - -export { - /** @deprecated AddFromLibraryButton - use `AddFromLibraryButton` from `@kbn/shared-ux-button-toolbar` */ - AddFromLibraryButton, - /** @deprecated PrimaryActionButton - use `PrimaryButton` from `@kbn/shared-ux-button-toolbar` */ - PrimaryActionButton, - /** @deprecated SolutionToolbarPopover - use `ToolbarPopover` from `@kbn/shared-ux-button-toolbar` */ - PrimaryActionPopover, - /** @deprecated QuickButtonGroup - use `IconButtonGroup` from `@kbn/shared-ux-button-toolbar` */ - QuickButtonGroup, - SolutionToolbar, - /** @deprecated SolutionToolbarButton - use `PrimaryButton` from `@kbn/shared-ux-button-toolbar` */ - SolutionToolbarButton, - /** @deprecated SolutionToolbarPopover - use `ToolbarPopover` from `@kbn/shared-ux-button-toolbar` */ - SolutionToolbarPopover, -} from './components/solution_toolbar'; - /** * Register a set of Expression Functions with the Presentation Utility ExpressionInput. This allows * the Monaco Editor to understand the functions and their arguments. diff --git a/src/plugins/saved_objects/public/finder/saved_object_finder.tsx b/src/plugins/saved_objects/public/finder/saved_object_finder.tsx index 4c39d02dd55f2..14fc712002abd 100644 --- a/src/plugins/saved_objects/public/finder/saved_object_finder.tsx +++ b/src/plugins/saved_objects/public/finder/saved_object_finder.tsx @@ -27,6 +27,9 @@ import { EuiSpacer, EuiTablePagination, IconType, + EuiFormRow, + EuiFieldSearchProps, + EuiFormRowProps, } from '@elastic/eui'; import { Direction } from '@elastic/eui/src/services/sort/sort_direction'; import { i18n } from '@kbn/i18n'; @@ -80,6 +83,8 @@ interface BaseSavedObjectFinder { noItemsMessage?: React.ReactNode; savedObjectMetaData: Array>; showFilter?: boolean; + euiFormRowProps?: Partial; + euiFieldSearchProps?: EuiFieldSearchProps; } interface SavedObjectFinderFixedPage extends BaseSavedObjectFinder { @@ -110,6 +115,13 @@ class SavedObjectFinderUi extends React.Component< initialPageSize: PropTypes.oneOf([5, 10, 15, 25]), fixedPageSize: PropTypes.number, showFilter: PropTypes.bool, + euiFormRowProps: PropTypes.object, + euiFieldSearchProps: PropTypes.object, + }; + + static defaultProps = { + euiFormRowProps: {}, + euiFieldSearchProps: {}, }; private isComponentMounted: boolean = false; @@ -336,109 +348,114 @@ class SavedObjectFinderUi extends React.Component< const availableSavedObjectMetaData = this.getAvailableSavedObjectMetaData(); return ( - - - { - this.setState( - { - query: e.target.value, - }, - this.fetchItems - ); - }} - data-test-subj="savedObjectFinderSearchInput" - isLoading={this.state.isFetchingItems} - /> - - - - this.setState({ sortOpen: false })} - button={ - - this.setState(({ sortOpen }) => ({ - sortOpen: !sortOpen, - })) - } - iconType="arrowDown" - isSelected={this.state.sortOpen} - data-test-subj="savedObjectFinderSortButton" - > - {i18n.translate('savedObjects.finder.sortButtonLabel', { - defaultMessage: 'Sort', - })} - - } - > - - - {this.props.showFilter && ( + + + + { + this.setState( + { + query: e.target.value, + }, + this.fetchItems + ); + }} + data-test-subj="savedObjectFinderSearchInput" + isLoading={this.state.isFetchingItems} + {...this.props.euiFieldSearchProps} + /> + + + this.setState({ filterOpen: false })} + isOpen={this.state.sortOpen} + closePopover={() => this.setState({ sortOpen: false })} button={ - this.setState(({ filterOpen }) => ({ - filterOpen: !filterOpen, + this.setState(({ sortOpen }) => ({ + sortOpen: !sortOpen, })) } iconType="arrowDown" - data-test-subj="savedObjectFinderFilterButton" - isSelected={this.state.filterOpen} - numFilters={this.props.savedObjectMetaData.length} - hasActiveFilters={this.state.filteredTypes.length > 0} - numActiveFilters={this.state.filteredTypes.length} + isSelected={this.state.sortOpen} + data-test-subj="savedObjectFinderSortButton" > - {i18n.translate('savedObjects.finder.filterButtonLabel', { - defaultMessage: 'Types', + {i18n.translate('savedObjects.finder.sortButtonLabel', { + defaultMessage: 'Sort', })} } > - ( - { - this.setState(({ filteredTypes }) => ({ - filteredTypes: filteredTypes.includes(metaData.type) - ? filteredTypes.filter((t) => t !== metaData.type) - : [...filteredTypes, metaData.type], - page: 0, - })); - }} - > - {metaData.name} - - ))} - /> + - )} - - - {this.props.children ? {this.props.children} : null} - + {this.props.showFilter && ( + this.setState({ filterOpen: false })} + button={ + + this.setState(({ filterOpen }) => ({ + filterOpen: !filterOpen, + })) + } + iconType="arrowDown" + data-test-subj="savedObjectFinderFilterButton" + isSelected={this.state.filterOpen} + numFilters={this.props.savedObjectMetaData.length} + hasActiveFilters={this.state.filteredTypes.length > 0} + numActiveFilters={this.state.filteredTypes.length} + > + {i18n.translate('savedObjects.finder.filterButtonLabel', { + defaultMessage: 'Types', + })} + + } + > + ( + { + this.setState(({ filteredTypes }) => ({ + filteredTypes: filteredTypes.includes(metaData.type) + ? filteredTypes.filter((t) => t !== metaData.type) + : [...filteredTypes, metaData.type], + page: 0, + })); + }} + > + {metaData.name} + + ))} + /> + + )} + + + {this.props.children ? ( + {this.props.children} + ) : null} + + ); } diff --git a/src/plugins/usage_collection/server/routes/stats/README.md b/src/plugins/usage_collection/server/routes/stats/README.md index 09dabefbab44a..48ebda9cecb05 100644 --- a/src/plugins/usage_collection/server/routes/stats/README.md +++ b/src/plugins/usage_collection/server/routes/stats/README.md @@ -8,9 +8,9 @@ However, the information detailed above can be extended, with the combination of | Query Parameter | Default value | Description | |:----------------|:-------------:|:------------| -|`extended`|`false`|When `true`, it adds `clusterUuid` and `usage`. The latter contains the information reported by all the Usage Collectors registered in the Kibana server. It may throw `503 Stats not ready` if any of the collectors is not fully initialized yet.| -|`legacy`|`false`|By default, when `extended=true`, the key names of the data in `usage` are transformed into API-friendlier `snake_case` format (i.e.: `clusterUuid` is transformed to `cluster_uuid`). When this parameter is `true`, the data is returned as-is.| -|`exclude_usage`|`false`|When `true`, and `extended=true`, it will report `clusterUuid` but no `usage`.| +|`extended`|`false`|When `true`, it adds `clusterUuid`.| +|`legacy`|`false`|By default, when `extended=true`, the key names are transformed into API-friendlier `snake_case` format (i.e.: `clusterUuid` is transformed to `cluster_uuid`). When this parameter is `true`, the data is returned as-is.| +|`exclude_usage`|`true`| Deprecated. Only kept for backward-compatibility. Setting this to `false` has no effect. Usage is always excluded. | ## Known use cases diff --git a/src/plugins/usage_collection/server/routes/stats/stats.ts b/src/plugins/usage_collection/server/routes/stats/stats.ts index 9db7f5a8638af..242e93a7554e3 100644 --- a/src/plugins/usage_collection/server/routes/stats/stats.ts +++ b/src/plugins/usage_collection/server/routes/stats/stats.ts @@ -8,14 +8,12 @@ import { schema } from '@kbn/config-schema'; import { i18n } from '@kbn/i18n'; -import defaultsDeep from 'lodash/defaultsDeep'; import { firstValueFrom, Observable } from 'rxjs'; import { ElasticsearchClient, IRouter, type MetricsServiceSetup, - SavedObjectsClientContract, ServiceStatus, ServiceStatusLevels, } from '@kbn/core/server'; @@ -51,14 +49,6 @@ export function registerStatsRoute({ metrics: MetricsServiceSetup; overallStatus$: Observable; }) { - const getUsage = async ( - esClient: ElasticsearchClient, - savedObjectsClient: SavedObjectsClientContract - ): Promise => { - const usage = await collectorSet.bulkFetchUsage(esClient, savedObjectsClient); - return collectorSet.toObject(usage); - }; - const getClusterUuid = async (asCurrentUser: ElasticsearchClient): Promise => { const body = await asCurrentUser.info({ filter_path: 'cluster_uuid' }); const { cluster_uuid: uuid } = body; @@ -77,7 +67,7 @@ export function registerStatsRoute({ extended: schema.oneOf([schema.literal(''), schema.boolean()], { defaultValue: false }), legacy: schema.oneOf([schema.literal(''), schema.boolean()], { defaultValue: false }), exclude_usage: schema.oneOf([schema.literal(''), schema.boolean()], { - defaultValue: false, + defaultValue: true, }), }), }, @@ -85,61 +75,26 @@ export function registerStatsRoute({ async (context, req, res) => { const isExtended = req.query.extended === '' || req.query.extended; const isLegacy = req.query.legacy === '' || req.query.legacy; - const shouldGetUsage = req.query.exclude_usage === false; let extended; if (isExtended) { const core = await context.core; const { asCurrentUser } = core.elasticsearch.client; - const savedObjectsClient = core.savedObjects.client; - - const [usage, clusterUuid] = await Promise.all([ - shouldGetUsage - ? getUsage(asCurrentUser, savedObjectsClient) - : Promise.resolve({}), - getClusterUuid(asCurrentUser), - ]); - - let modifiedUsage = usage; - if (isLegacy) { - // In an effort to make telemetry more easily augmented, we need to ensure - // we can passthrough the data without every part of the process needing - // to know about the change; however, to support legacy use cases where this - // wasn't true, we need to be backwards compatible with how the legacy data - // looked and support those use cases here. - modifiedUsage = Object.keys(usage).reduce((accum, usageKey) => { - if (usageKey === 'kibana') { - accum = { - ...accum, - ...usage[usageKey], - }; - } else if (usageKey === 'reporting') { - accum = { - ...accum, - xpack: { - ...accum.xpack, - reporting: usage[usageKey], - }, - }; - } else { - // I don't think we need to it this for the above conditions, but do it for most as it will - // match the behavior done in monitoring/bulk_uploader - defaultsDeep(accum, { [usageKey]: usage[usageKey] }); - } - return accum; - }, {} as UsageObject); + const usage = {} as UsageObject; + const clusterUuid = await getClusterUuid(asCurrentUser); - extended = { - usage: modifiedUsage, - clusterUuid, - }; - } else { - extended = collectorSet.toApiFieldNames({ - usage: modifiedUsage, - clusterUuid, - }); - } + // In an effort to make telemetry more easily augmented, we need to ensure + // we can passthrough the data without every part of the process needing + // to know about the change; however, to support legacy use cases where this + // wasn't true, we need to be backwards compatible with how the legacy data + // looked and support those use cases here. + extended = isLegacy + ? { usage, clusterUuid } + : collectorSet.toApiFieldNames({ + usage, + clusterUuid, + }); } // Guaranteed to resolve immediately due to replay effect on getOpsMetrics$ diff --git a/test/api_integration/apis/stats/stats.js b/test/api_integration/apis/stats/stats.js index a95204b5fff4a..3d69a949a4db3 100644 --- a/test/api_integration/apis/stats/stats.js +++ b/test/api_integration/apis/stats/stats.js @@ -91,6 +91,7 @@ export default function ({ getService }) { .then(({ body }) => { expect(body.cluster_uuid).to.be.a('string'); expect(body.usage).to.be.an('object'); // no usage collectors have been registered so usage is an empty object + expect(body.usage).to.eql({}); assertStatsAndMetrics(body); }); }); @@ -103,6 +104,7 @@ export default function ({ getService }) { .then(({ body }) => { expect(body.cluster_uuid).to.be.a('string'); expect(body.usage).to.be.an('object'); + expect(body.usage).to.eql({}); assertStatsAndMetrics(body); }); }); @@ -116,6 +118,7 @@ export default function ({ getService }) { .then(({ body }) => { expect(body.clusterUuid).to.be.a('string'); expect(body.usage).to.be.an('object'); // no usage collectors have been registered so usage is an empty object + expect(body.usage).to.eql({}); assertStatsAndMetrics(body, true); }); }); diff --git a/test/examples/config.js b/test/examples/config.js index fc57b610941d2..2dce755e413b7 100644 --- a/test/examples/config.js +++ b/test/examples/config.js @@ -28,6 +28,7 @@ export default async function ({ readConfigFile }) { require.resolve('./field_formats'), require.resolve('./partial_results'), require.resolve('./search'), + require.resolve('./content_management'), ], services: { ...functionalConfig.get('services'), diff --git a/test/examples/content_management/index.ts b/test/examples/content_management/index.ts new file mode 100644 index 0000000000000..d5e3f14bfc0e6 --- /dev/null +++ b/test/examples/content_management/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { PluginFunctionalProviderContext } from '../../plugin_functional/services'; + +// eslint-disable-next-line import/no-default-export +export default function ({ loadTestFile }: PluginFunctionalProviderContext) { + describe('content management examples', function () { + loadTestFile(require.resolve('./todo_app')); + }); +} diff --git a/test/examples/content_management/todo_app.ts b/test/examples/content_management/todo_app.ts new file mode 100644 index 0000000000000..5c8228540a2de --- /dev/null +++ b/test/examples/content_management/todo_app.ts @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import expect from '@kbn/expect'; +import { Key } from 'selenium-webdriver'; + +import { PluginFunctionalProviderContext } from '../../plugin_functional/services'; + +// eslint-disable-next-line import/no-default-export +export default function ({ getService, getPageObjects }: PluginFunctionalProviderContext) { + const testSubjects = getService('testSubjects'); + const find = getService('find'); + const retry = getService('retry'); + const PageObjects = getPageObjects(['common']); + + describe('Todo app', () => { + it('Todo app works', async () => { + const appId = 'contentManagementExamples'; + await PageObjects.common.navigateToApp(appId); + + // check that initial state is correct + let todos = await testSubjects.findAll(`~todoItem`); + expect(todos.length).to.be(2); + + // check that filters work + await (await find.byCssSelector('label[title="Completed"]')).click(); + todos = await testSubjects.findAll(`~todoItem`); + expect(todos.length).to.be(1); + + await (await find.byCssSelector('label[title="Todo"]')).click(); + todos = await testSubjects.findAll(`~todoItem`); + expect(todos.length).to.be(1); + + await (await find.byCssSelector('label[title="All"]')).click(); + todos = await testSubjects.findAll(`~todoItem`); + expect(todos.length).to.be(2); + + // check that adding new todo works + await testSubjects.setValue('newTodo', 'New todo'); + await (await testSubjects.find('newTodo')).pressKeys(Key.ENTER); + await retry.tryForTime(1000, async () => { + todos = await testSubjects.findAll(`~todoItem`); + expect(todos.length).to.be(3); + }); + + // check that updating todo works + let newTodo = todos[2]; + expect(await newTodo.getVisibleText()).to.be('New todo'); + let newTodoCheckbox = await newTodo.findByTestSubject('~todoCheckbox'); + expect(await newTodoCheckbox.isSelected()).to.be(false); + await (await newTodo.findByTagName('label')).click(); + + await (await find.byCssSelector('label[title="Completed"]')).click(); + todos = await testSubjects.findAll(`~todoItem`); + expect(todos.length).to.be(2); + newTodo = todos[1]; + expect(await newTodo.getVisibleText()).to.be('New todo'); + newTodoCheckbox = await newTodo.findByTestSubject('~todoCheckbox'); + expect(await newTodoCheckbox.isSelected()).to.be(true); + + // check that deleting todo works + await (await newTodo.findByCssSelector('[aria-label="Delete"]')).click(); + todos = await testSubjects.findAll(`~todoItem`); + expect(todos.length).to.be(1); + }); + }); +} diff --git a/test/functional/apps/console/_xjson.ts b/test/functional/apps/console/_xjson.ts index 1535337a2a848..445bfa5d42f0a 100644 --- a/test/functional/apps/console/_xjson.ts +++ b/test/functional/apps/console/_xjson.ts @@ -15,7 +15,8 @@ export default ({ getService, getPageObjects }: FtrProviderContext) => { const log = getService('log'); const PageObjects = getPageObjects(['common', 'console', 'header']); - describe('XJSON', function testXjson() { + // FLAKY: https://github.com/elastic/kibana/issues/145477 + describe.skip('XJSON', function testXjson() { this.tags('includeFirefox'); before(async () => { await PageObjects.common.navigateToApp('console'); diff --git a/test/functional/page_objects/dashboard_page_controls.ts b/test/functional/page_objects/dashboard_page_controls.ts index c3160e650c2a8..ff4ac5b67f804 100644 --- a/test/functional/page_objects/dashboard_page_controls.ts +++ b/test/functional/page_objects/dashboard_page_controls.ts @@ -576,7 +576,7 @@ export class DashboardPageControls extends FtrService { public async controlsEditorSetfield( fieldName: string, expectedType?: string, - shouldSearch: boolean = false + shouldSearch: boolean = true ) { this.log.debug(`Setting control field to ${fieldName}`); if (shouldSearch) { diff --git a/test/harden/lodash_template.js b/test/harden/lodash_template.js index b04f8ad66275a..49cf7351972e8 100644 --- a/test/harden/lodash_template.js +++ b/test/harden/lodash_template.js @@ -11,6 +11,7 @@ const _ = require('lodash'); // eslint-disable-next-line no-restricted-modules const template = require('lodash/template'); const fp = require('lodash/fp'); +// eslint-disable-next-line no-restricted-modules const fpTemplate = require('lodash/fp/template'); const test = require('tape'); diff --git a/test/node_roles_functional/test_suites/all/initializer_context.ts b/test/node_roles_functional/test_suites/all/initializer_context.ts index e616ec8243c9d..43407b7763b66 100644 --- a/test/node_roles_functional/test_suites/all/initializer_context.ts +++ b/test/node_roles_functional/test_suites/all/initializer_context.ts @@ -16,6 +16,7 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide it('passes node roles to server PluginInitializerContext', async () => { await supertest.get('/core_plugin_initializer_context/node/roles').expect(200, { backgroundTasks: true, + migrator: false, ui: true, }); }); diff --git a/test/node_roles_functional/test_suites/background_tasks/initializer_context.ts b/test/node_roles_functional/test_suites/background_tasks/initializer_context.ts index e616ec8243c9d..43407b7763b66 100644 --- a/test/node_roles_functional/test_suites/background_tasks/initializer_context.ts +++ b/test/node_roles_functional/test_suites/background_tasks/initializer_context.ts @@ -16,6 +16,7 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide it('passes node roles to server PluginInitializerContext', async () => { await supertest.get('/core_plugin_initializer_context/node/roles').expect(200, { backgroundTasks: true, + migrator: false, ui: true, }); }); diff --git a/test/node_roles_functional/test_suites/ui/initializer_context.ts b/test/node_roles_functional/test_suites/ui/initializer_context.ts index 7d4c4f5ada527..28233cfc73c93 100644 --- a/test/node_roles_functional/test_suites/ui/initializer_context.ts +++ b/test/node_roles_functional/test_suites/ui/initializer_context.ts @@ -16,6 +16,7 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide it('passes node roles to server PluginInitializerContext', async () => { await supertest.get('/core_plugin_initializer_context/node/roles').expect(200, { backgroundTasks: false, + migrator: false, ui: true, }); }); diff --git a/tsconfig.base.json b/tsconfig.base.json index cff7037533bfe..1e8bd1ef0ade0 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -32,6 +32,8 @@ "@kbn/alerting-plugin/*": ["x-pack/plugins/alerting/*"], "@kbn/alerts": ["packages/kbn-alerts"], "@kbn/alerts/*": ["packages/kbn-alerts/*"], + "@kbn/alerts-as-data-utils": ["packages/kbn-alerts-as-data-utils"], + "@kbn/alerts-as-data-utils/*": ["packages/kbn-alerts-as-data-utils/*"], "@kbn/alerts-restricted-fixtures-plugin": ["x-pack/test/alerting_api_integration/common/plugins/alerts_restricted"], "@kbn/alerts-restricted-fixtures-plugin/*": ["x-pack/test/alerting_api_integration/common/plugins/alerts_restricted/*"], "@kbn/alerts-ui-shared": ["packages/kbn-alerts-ui-shared"], @@ -158,6 +160,8 @@ "@kbn/console-plugin/*": ["src/plugins/console/*"], "@kbn/content-management-content-editor": ["packages/content-management/content_editor"], "@kbn/content-management-content-editor/*": ["packages/content-management/content_editor/*"], + "@kbn/content-management-examples-plugin": ["examples/content_management_examples"], + "@kbn/content-management-examples-plugin/*": ["examples/content_management_examples/*"], "@kbn/content-management-plugin": ["src/plugins/content_management"], "@kbn/content-management-plugin/*": ["src/plugins/content_management/*"], "@kbn/content-management-table-list": ["packages/content-management/table_list"], @@ -666,6 +670,8 @@ "@kbn/event-log-fixture-plugin/*": ["x-pack/test/plugin_api_integration/plugins/event_log/*"], "@kbn/event-log-plugin": ["x-pack/plugins/event_log"], "@kbn/event-log-plugin/*": ["x-pack/plugins/event_log/*"], + "@kbn/expandable-flyout": ["packages/kbn-expandable-flyout"], + "@kbn/expandable-flyout/*": ["packages/kbn-expandable-flyout/*"], "@kbn/expect": ["packages/kbn-expect"], "@kbn/expect/*": ["packages/kbn-expect/*"], "@kbn/exploratory-view-example-plugin": ["x-pack/examples/exploratory_view_example"], diff --git a/x-pack/performance/configs/cloud_security_posture_config.ts b/x-pack/performance/configs/cloud_security_posture_config.ts new file mode 100644 index 0000000000000..3d4f36bb2c605 --- /dev/null +++ b/x-pack/performance/configs/cloud_security_posture_config.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 { FtrConfigProviderContext } from '@kbn/test'; + +// eslint-disable-next-line import/no-default-export +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const xpackFunctionalConfig = await readConfigFile( + // eslint-disable-next-line @kbn/imports/no_boundary_crossing + require.resolve('../../test/functional/config.base.js') + ); + + return { + ...xpackFunctionalConfig.getAll(), + kbnTestServer: { + ...xpackFunctionalConfig.get('kbnTestServer'), + serverArgs: [ + ...xpackFunctionalConfig.get('kbnTestServer.serverArgs'), + /** + * Package version is fixed (not latest) so FTR won't suddenly break when package is changed. + * + * test a new package: + * 1. build the package and start the registry with elastic-package and uncomment the 'registryUrl' flag below + * 2. locally checkout the kibana version that matches the new package + * 3. update the package version below to use the new package version + * 4. run tests with NODE_EXTRA_CA_CERTS pointing to the elastic-package certificate. example: + * NODE_EXTRA_CA_CERTS=HOME/.elastic-package/profiles/default/certs/kibana/ca-cert.pem yarn start + * 5. when test pass: + * 1. release a new package to EPR + * 2. merge the updated version number change to kibana + */ + `--xpack.fleet.packages.0.name=cloud_security_posture`, + `--xpack.fleet.packages.0.version=1.2.8`, + // `--xpack.fleet.registryUrl=https://localhost:8080`, + ], + }, + }; +} diff --git a/x-pack/performance/es_archives/kspm_findings/data.json.gz b/x-pack/performance/es_archives/kspm_findings/data.json.gz new file mode 100644 index 0000000000000..075885aacbb45 Binary files /dev/null and b/x-pack/performance/es_archives/kspm_findings/data.json.gz differ diff --git a/x-pack/performance/es_archives/kspm_findings/mappings.json b/x-pack/performance/es_archives/kspm_findings/mappings.json new file mode 100644 index 0000000000000..148507b88cc73 --- /dev/null +++ b/x-pack/performance/es_archives/kspm_findings/mappings.json @@ -0,0 +1,464 @@ +{ + "type": "index", + "value": { + "aliases": {}, + "index": "logs-cloud_security_posture.findings_latest-default", + "mappings": { + "_meta": { + "managed": true, + "managed_by": "fleet", + "package": { + "name": "cloud_security_posture" + } + }, + "date_detection": false, + "dynamic": "false", + "dynamic_templates": [ + { + "strings_as_keyword": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" + } + } + ], + "properties": { + "@timestamp": { + "type": "date" + }, + "agent": { + "properties": { + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "cloud": { + "properties": { + "account": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "cloudbeat": { + "properties": { + "commit_sha": { + "ignore_above": 1024, + "type": "keyword" + }, + "commit_time": { + "type": "date" + }, + "kubernetes": { + "properties": { + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "policy": { + "properties": { + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "cluster_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "data_stream": { + "properties": { + "dataset": { + "type": "constant_keyword", + "value": "cloud_security_posture.findings" + }, + "namespace": { + "type": "constant_keyword", + "value": "default" + }, + "type": { + "type": "constant_keyword", + "value": "logs" + } + } + }, + "ecs": { + "properties": { + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "event": { + "properties": { + "agent_id_status": { + "ignore_above": 1024, + "type": "keyword" + }, + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "created": { + "type": "date" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ingested": { + "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", + "type": "date" + }, + "kind": { + "ignore_above": 1024, + "type": "keyword" + }, + "outcome": { + "ignore_above": 1024, + "type": "keyword" + }, + "sequence": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "file": { + "properties": { + "accessed": { + "type": "date" + }, + "ctime": { + "type": "date" + }, + "directory": { + "ignore_above": 1024, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "inode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mtime": { + "type": "date" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "owner": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "size": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "containerized": { + "type": "boolean" + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "codename": { + "ignore_above": 1024, + "type": "keyword" + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "message": { + "type": "match_only_text" + }, + "orchestrator": { + "properties": { + "cluster": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "process": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "command_line": { + "ignore_above": 1024, + "type": "wildcard" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "parent": { + "properties": { + "pid": { + "type": "long" + }, + "start": { + "type": "date" + } + } + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "title": { + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + } + } + }, + "resource": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "raw": { + "enabled": false, + "type": "object" + }, + "sub_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "result": { + "properties": { + "evaluation": { + "ignore_above": 1024, + "type": "keyword" + }, + "evidence": { + "enabled": false, + "type": "object" + }, + "expected": { + "enabled": false, + "type": "object" + } + } + }, + "rule": { + "properties": { + "benchmark": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "posture_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "rule_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "section": { + "ignore_above": 1024, + "type": "keyword" + }, + "tags": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "settings": { + "index": { + "codec": "best_compression", + "lifecycle": { + "name": "" + }, + "mapping": { + "total_fields": { + "limit": "10000" + } + }, + "number_of_replicas": "1", + "number_of_shards": "1" + } + } + } +} diff --git a/x-pack/performance/journeys/cloud_security_dashboard.ts b/x-pack/performance/journeys/cloud_security_dashboard.ts new file mode 100644 index 0000000000000..39625126e7067 --- /dev/null +++ b/x-pack/performance/journeys/cloud_security_dashboard.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 } from '@kbn/journeys'; +import expect from '@kbn/expect'; + +export const journey = new Journey({ + beforeSteps: async ({ kibanaServer, retry }) => { + await retry.try(async () => { + const response = await kibanaServer.request({ + path: '/internal/cloud_security_posture/status?check=init', + method: 'GET', + }); + expect(response.status).to.eql(200); + expect(response.data).to.eql({ isPluginInitialized: true }); + }); + }, + ftrConfigPath: 'x-pack/performance/configs/cloud_security_posture_config.ts', + esArchives: ['x-pack/performance/es_archives/kspm_findings'], + scalabilitySetup: { + warmup: [ + { + action: 'constantConcurrentUsers', + userCount: 10, + duration: '30s', + }, + { + action: 'rampConcurrentUsers', + minUsersCount: 10, + maxUsersCount: 50, + duration: '2m', + }, + ], + test: [ + { + action: 'constantConcurrentUsers', + userCount: 50, + duration: '3m', + }, + ], + maxDuration: '10m', + }, +}).step('Go to cloud security dashboards Page', async ({ page, kbnUrl }) => { + await page.goto(kbnUrl.get(`/app/security/cloud_security_posture/dashboard`)); + await page.waitForSelector(`[data-test-subj="csp:dashboard-sections-table-header-score"]`); +}); diff --git a/x-pack/performance/tsconfig.json b/x-pack/performance/tsconfig.json index 636c4e1f5ed1f..fb60be2e310ce 100644 --- a/x-pack/performance/tsconfig.json +++ b/x-pack/performance/tsconfig.json @@ -12,5 +12,7 @@ "@kbn/journeys", "@kbn/test-subj-selector", "@kbn/tooling-log", + "@kbn/test", + "@kbn/expect", ] } diff --git a/x-pack/plugins/actions/server/lib/action_executor.test.ts b/x-pack/plugins/actions/server/lib/action_executor.test.ts index ecb99eaf9d852..dd7cdbcb45124 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.test.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.test.ts @@ -14,13 +14,11 @@ import { loggingSystemMock } from '@kbn/core/server/mocks'; import { eventLoggerMock } from '@kbn/event-log-plugin/server/mocks'; import { spacesServiceMock } from '@kbn/spaces-plugin/server/spaces_service/spaces_service.mock'; import { ActionType } from '../types'; -import { actionsMock, actionsClientMock } from '../mocks'; -import { pick } from 'lodash'; +import { actionsMock } from '../mocks'; const actionExecutor = new ActionExecutor({ isESOCanEncrypt: true }); const services = actionsMock.createServices(); -const actionsClient = actionsClientMock.create(); const encryptedSavedObjectsClient = encryptedSavedObjectsMock.createClient(); const actionTypeRegistry = actionTypeRegistryMock.create(); const eventLogger = eventLoggerMock.create(); @@ -39,12 +37,10 @@ const spacesMock = spacesServiceMock.createStartContract(); const loggerMock: ReturnType = loggingSystemMock.createLogger(); -const getActionsClientWithRequest = jest.fn(); actionExecutor.initialize({ logger: loggerMock, spaces: spacesMock, getServices: () => services, - getActionsClientWithRequest, actionTypeRegistry, encryptedSavedObjectsClient, eventLogger, @@ -68,7 +64,6 @@ actionExecutor.initialize({ beforeEach(() => { jest.resetAllMocks(); spacesMock.getSpaceId.mockReturnValue('some-namespace'); - getActionsClientWithRequest.mockResolvedValue(actionsClient); loggerMock.get.mockImplementation(() => loggerMock); }); @@ -84,6 +79,7 @@ test('successfully executes', async () => { id: '1', type: 'action', attributes: { + name: '1', actionTypeId: 'test', config: { bar: true, @@ -94,14 +90,6 @@ test('successfully executes', async () => { }, references: [], }; - const actionResult = { - id: actionSavedObject.id, - name: actionSavedObject.id, - ...pick(actionSavedObject.attributes, 'actionTypeId', 'config'), - isPreconfigured: false, - isDeprecated: false, - }; - actionsClient.get.mockResolvedValueOnce(actionResult); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(actionSavedObject); actionTypeRegistry.get.mockReturnValueOnce(actionType); await actionExecutor.execute(executeParams); @@ -224,7 +212,6 @@ test('successfully executes with preconfigured connector', async () => { actionTypeRegistry.get.mockReturnValueOnce(actionType); await actionExecutor.execute({ ...executeParams, actionId: 'preconfigured' }); - expect(actionsClient.get).not.toHaveBeenCalled(); expect(encryptedSavedObjectsClient.getDecryptedAsInternalUser).not.toHaveBeenCalled(); expect(actionTypeRegistry.get).toHaveBeenCalledWith('test'); @@ -341,6 +328,7 @@ test('successfully executes as a task', async () => { id: '1', type: 'action', attributes: { + name: '1', actionTypeId: 'test', config: { bar: true, @@ -351,14 +339,6 @@ test('successfully executes as a task', async () => { }, references: [], }; - const actionResult = { - id: actionSavedObject.id, - name: actionSavedObject.id, - ...pick(actionSavedObject.attributes, 'actionTypeId', 'config'), - isPreconfigured: false, - isDeprecated: false, - }; - actionsClient.get.mockResolvedValueOnce(actionResult); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(actionSavedObject); actionTypeRegistry.get.mockReturnValueOnce(actionType); @@ -392,18 +372,11 @@ test('provides empty config when config and / or secrets is empty', async () => id: '1', type: 'action', attributes: { + name: '1', actionTypeId: 'test', }, references: [], }; - const actionResult = { - id: actionSavedObject.id, - name: actionSavedObject.id, - actionTypeId: actionSavedObject.attributes.actionTypeId, - isPreconfigured: false, - isDeprecated: false, - }; - actionsClient.get.mockResolvedValueOnce(actionResult); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(actionSavedObject); actionTypeRegistry.get.mockReturnValueOnce(actionType); await actionExecutor.execute(executeParams); @@ -432,18 +405,11 @@ test('throws an error when config is invalid', async () => { id: '1', type: 'action', attributes: { + name: '1', actionTypeId: 'test', }, references: [], }; - const actionResult = { - id: actionSavedObject.id, - name: actionSavedObject.id, - actionTypeId: actionSavedObject.attributes.actionTypeId, - isPreconfigured: false, - isDeprecated: false, - }; - actionsClient.get.mockResolvedValueOnce(actionResult); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(actionSavedObject); actionTypeRegistry.get.mockReturnValueOnce(actionType); @@ -473,18 +439,11 @@ test('throws an error when connector is invalid', async () => { id: '1', type: 'action', attributes: { + name: '1', actionTypeId: 'test', }, references: [], }; - const actionResult = { - id: actionSavedObject.id, - name: actionSavedObject.id, - actionTypeId: actionSavedObject.attributes.actionTypeId, - isPreconfigured: false, - isDeprecated: false, - }; - actionsClient.get.mockResolvedValueOnce(actionResult); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(actionSavedObject); actionTypeRegistry.get.mockReturnValueOnce(actionType); @@ -516,18 +475,11 @@ test('throws an error when params is invalid', async () => { id: '1', type: 'action', attributes: { + name: '1', actionTypeId: 'test', }, references: [], }; - const actionResult = { - id: actionSavedObject.id, - name: actionSavedObject.id, - actionTypeId: actionSavedObject.attributes.actionTypeId, - isPreconfigured: false, - isDeprecated: false, - }; - actionsClient.get.mockResolvedValueOnce(actionResult); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(actionSavedObject); actionTypeRegistry.get.mockReturnValueOnce(actionType); @@ -541,7 +493,9 @@ test('throws an error when params is invalid', async () => { }); test('throws an error when failing to load action through savedObjectsClient', async () => { - actionsClient.get.mockRejectedValueOnce(new Error('No access')); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockRejectedValueOnce( + new Error('No access') + ); await expect(actionExecutor.execute(executeParams)).rejects.toThrowErrorMatchingInlineSnapshot( `"No access"` ); @@ -559,18 +513,11 @@ test('throws an error if actionType is not enabled', async () => { id: '1', type: 'action', attributes: { + name: '1', actionTypeId: 'test', }, references: [], }; - const actionResult = { - id: actionSavedObject.id, - name: actionSavedObject.id, - actionTypeId: actionSavedObject.attributes.actionTypeId, - isPreconfigured: false, - isDeprecated: false, - }; - actionsClient.get.mockResolvedValueOnce(actionResult); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(actionSavedObject); actionTypeRegistry.get.mockReturnValueOnce(actionType); actionTypeRegistry.ensureActionTypeEnabled.mockImplementationOnce(() => { @@ -595,6 +542,7 @@ test('should not throws an error if actionType is preconfigured', async () => { id: '1', type: 'action', attributes: { + name: '1', actionTypeId: 'test', config: { bar: true, @@ -605,14 +553,6 @@ test('should not throws an error if actionType is preconfigured', async () => { }, references: [], }; - const actionResult = { - id: actionSavedObject.id, - name: actionSavedObject.id, - ...pick(actionSavedObject.attributes, 'actionTypeId', 'config', 'secrets'), - isPreconfigured: false, - isDeprecated: false, - }; - actionsClient.get.mockResolvedValueOnce(actionResult); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(actionSavedObject); actionTypeRegistry.get.mockReturnValueOnce(actionType); actionTypeRegistry.ensureActionTypeEnabled.mockImplementationOnce(() => { @@ -641,7 +581,6 @@ test('throws an error when passing isESOCanEncrypt with value of false', async ( customActionExecutor.initialize({ logger: loggingSystemMock.create().get(), spaces: spacesMock, - getActionsClientWithRequest, getServices: () => services, actionTypeRegistry, encryptedSavedObjectsClient, @@ -660,7 +599,6 @@ test('should not throw error if action is preconfigured and isESOCanEncrypt is f customActionExecutor.initialize({ logger: loggingSystemMock.create().get(), spaces: spacesMock, - getActionsClientWithRequest, getServices: () => services, actionTypeRegistry, encryptedSavedObjectsClient, @@ -692,7 +630,6 @@ test('should not throw error if action is preconfigured and isESOCanEncrypt is f actionTypeRegistry.get.mockReturnValueOnce(actionType); await actionExecutor.execute({ ...executeParams, actionId: 'preconfigured' }); - expect(actionsClient.get).not.toHaveBeenCalled(); expect(encryptedSavedObjectsClient.getDecryptedAsInternalUser).not.toHaveBeenCalled(); expect(actionTypeRegistry.get).toHaveBeenCalledWith('test'); @@ -1018,8 +955,8 @@ function setupActionExecutorMock() { const actionSavedObject = { id: '1', type: 'action', - name: 'action-1', attributes: { + name: 'action-1', actionTypeId: 'test', config: { bar: true, @@ -1030,14 +967,6 @@ function setupActionExecutorMock() { }, references: [], }; - const actionResult = { - id: actionSavedObject.id, - name: actionSavedObject.name, - ...pick(actionSavedObject.attributes, 'actionTypeId', 'config'), - isPreconfigured: false, - isDeprecated: false, - }; - actionsClient.get.mockResolvedValueOnce(actionResult); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(actionSavedObject); actionTypeRegistry.get.mockReturnValueOnce(actionType); return actionType.executor; diff --git a/x-pack/plugins/actions/server/lib/action_executor.ts b/x-pack/plugins/actions/server/lib/action_executor.ts index 36212bc9ae042..d42951947f93d 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.ts @@ -29,7 +29,6 @@ import { ValidatorServices, } from '../types'; import { EVENT_LOG_ACTIONS } from '../constants/event_log'; -import { ActionsClient } from '../actions_client'; import { ActionExecutionSource } from './action_execution_source'; import { RelatedSavedObjects } from './related_saved_objects'; import { createActionEventLogRecordObject } from './create_action_event_log_record_object'; @@ -42,10 +41,6 @@ export interface ActionExecutorContext { logger: Logger; spaces?: SpacesServiceStart; getServices: GetServicesFunction; - getActionsClientWithRequest: ( - request: KibanaRequest, - authorizationContext?: ActionExecutionSource - ) => Promise>; encryptedSavedObjectsClient: EncryptedSavedObjectsClient; actionTypeRegistry: ActionTypeRegistryContract; eventLogger: IEventLogger; @@ -95,7 +90,6 @@ export class ActionExecutor { actionId, params, request, - source, isEphemeral, taskInfo, executionId, @@ -123,7 +117,6 @@ export class ActionExecutor { actionTypeRegistry, eventLogger, preconfiguredActions, - getActionsClientWithRequest, } = this.actionExecutorContext!; const services = getServices(request); @@ -131,14 +124,11 @@ export class ActionExecutor { const namespace = spaceId && spaceId !== 'default' ? { namespace: spaceId } : {}; const actionInfo = await getActionInfoInternal( - getActionsClientWithRequest, - request, this.isESOCanEncrypt, encryptedSavedObjectsClient, preconfiguredActions, actionId, - namespace.namespace, - source + namespace.namespace ); const { actionTypeId, name, config, secrets } = actionInfo; @@ -313,26 +303,18 @@ export class ActionExecutor { source?: ActionExecutionSource; consumer?: string; }) { - const { - spaces, - encryptedSavedObjectsClient, - preconfiguredActions, - eventLogger, - getActionsClientWithRequest, - } = this.actionExecutorContext!; + const { spaces, encryptedSavedObjectsClient, preconfiguredActions, eventLogger } = + this.actionExecutorContext!; const spaceId = spaces && spaces.getSpaceId(request); const namespace = spaceId && spaceId !== 'default' ? { namespace: spaceId } : {}; if (!this.actionInfo || this.actionInfo.actionId !== actionId) { this.actionInfo = await getActionInfoInternal( - getActionsClientWithRequest, - request, this.isESOCanEncrypt, encryptedSavedObjectsClient, preconfiguredActions, actionId, - namespace.namespace, - source + namespace.namespace ); } const task = taskInfo @@ -382,17 +364,11 @@ interface ActionInfo { } async function getActionInfoInternal( - getActionsClientWithRequest: ( - request: KibanaRequest, - authorizationContext?: ActionExecutionSource - ) => Promise>, - request: KibanaRequest, isESOCanEncrypt: boolean, encryptedSavedObjectsClient: EncryptedSavedObjectsClient, preconfiguredActions: PreConfiguredAction[], actionId: string, - namespace: string | undefined, - source?: ActionExecutionSource + namespace: string | undefined ): Promise { // check to see if it's a pre-configured action first const pcAction = preconfiguredActions.find( @@ -415,14 +391,8 @@ async function getActionInfoInternal( ); } - const actionsClient = await getActionsClientWithRequest(request, source); - - // if not pre-configured action, should be a saved object - // ensure user can read the action before processing - const { actionTypeId, config, name } = await actionsClient.get({ id: actionId }); - const { - attributes: { secrets }, + attributes: { secrets, actionTypeId, config, name }, } = await encryptedSavedObjectsClient.getDecryptedAsInternalUser('action', actionId, { namespace: namespace === 'default' ? undefined : namespace, }); diff --git a/x-pack/plugins/actions/server/plugin.ts b/x-pack/plugins/actions/server/plugin.ts index 21884b12cacd5..76e59fa370fc2 100644 --- a/x-pack/plugins/actions/server/plugin.ts +++ b/x-pack/plugins/actions/server/plugin.ts @@ -507,7 +507,6 @@ export class ActionsPlugin implements Plugin = ({ content={i18n.translate( 'xpack.aiops.explainLogRateSpikes.spikeAnalysisTable.impactLabelColumnTooltip', { - defaultMessage: 'The level of impact of the field on the message rate difference', + defaultMessage: 'The level of impact of the field on the message rate difference.', } )} > diff --git a/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table_groups.tsx b/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table_groups.tsx index 954770950e036..363aff283c4cc 100644 --- a/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table_groups.tsx +++ b/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table_groups.tsx @@ -328,7 +328,7 @@ export const SpikeAnalysisGroupsTable: FC = ({ 'xpack.aiops.explainLogRateSpikes.spikeAnalysisTableGroups.logRateColumnTooltip', { defaultMessage: - 'A visual representation of the impact of the group on the message rate difference', + 'A visual representation of the impact of the group on the message rate difference.', } )} > @@ -377,7 +377,7 @@ export const SpikeAnalysisGroupsTable: FC = ({ 'xpack.aiops.explainLogRateSpikes.spikeAnalysisTableGroups.pValueColumnTooltip', { defaultMessage: - 'The significance of changes in the frequency of values; lower values indicate greater change', + 'The significance of changes in the frequency of values; lower values indicate greater change.', } )} > diff --git a/x-pack/plugins/alerting/common/alert_schema/field_maps/component_template_from_field_map.ts b/x-pack/plugins/alerting/common/alert_schema/field_maps/component_template_from_field_map.ts index b4cd25a4f4126..4fc36193a15d9 100644 --- a/x-pack/plugins/alerting/common/alert_schema/field_maps/component_template_from_field_map.ts +++ b/x-pack/plugins/alerting/common/alert_schema/field_maps/component_template_from_field_map.ts @@ -6,8 +6,8 @@ */ import { ClusterPutComponentTemplateRequest } from '@elastic/elasticsearch/lib/api/types'; +import { type FieldMap } from '@kbn/alerts-as-data-utils'; import { mappingFromFieldMap } from './mapping_from_field_map'; -import { FieldMap } from './types'; export interface GetComponentTemplateFromFieldMapOpts { name: string; diff --git a/x-pack/plugins/alerting/common/alert_schema/field_maps/mapping_from_field_map.test.ts b/x-pack/plugins/alerting/common/alert_schema/field_maps/mapping_from_field_map.test.ts index 2f2cac2367e8b..f5eeeb8ba6c35 100644 --- a/x-pack/plugins/alerting/common/alert_schema/field_maps/mapping_from_field_map.test.ts +++ b/x-pack/plugins/alerting/common/alert_schema/field_maps/mapping_from_field_map.test.ts @@ -4,9 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { alertFieldMap, legacyAlertFieldMap, type FieldMap } from '@kbn/alerts-as-data-utils'; import { mappingFromFieldMap } from './mapping_from_field_map'; -import { FieldMap } from './types'; -import { alertFieldMap } from './alert_field_map'; describe('mappingFromFieldMap', () => { const fieldMap: FieldMap = { @@ -118,6 +117,15 @@ describe('mappingFromFieldMap', () => { date_field: { type: 'date', }, + multifield_field: { + fields: { + text: { + type: 'match_only_text', + }, + }, + ignore_above: 1024, + type: 'keyword', + }, geopoint_field: { type: 'geo_point', }, @@ -131,15 +139,6 @@ describe('mappingFromFieldMap', () => { long_field: { type: 'long', }, - multifield_field: { - fields: { - text: { - type: 'match_only_text', - }, - }, - ignore_above: 1024, - type: 'keyword', - }, nested_array_field: { properties: { field1: { @@ -184,6 +183,9 @@ describe('mappingFromFieldMap', () => { expect(mappingFromFieldMap(alertFieldMap)).toEqual({ dynamic: 'strict', properties: { + '@timestamp': { + type: 'date', + }, kibana: { properties: { alert: { @@ -191,6 +193,9 @@ describe('mappingFromFieldMap', () => { action_group: { type: 'keyword', }, + case_ids: { + type: 'keyword', + }, duration: { properties: { us: { @@ -204,8 +209,18 @@ describe('mappingFromFieldMap', () => { flapping: { type: 'boolean', }, - id: { - type: 'keyword', + flapping_history: { + type: 'boolean', + }, + instance: { + properties: { + id: { + type: 'keyword', + }, + }, + }, + last_detected: { + type: 'date', }, reason: { type: 'keyword', @@ -229,8 +244,8 @@ describe('mappingFromFieldMap', () => { type: 'keyword', }, parameters: { - type: 'object', - enabled: false, + type: 'flattened', + ignore_above: 4096, }, producer: { type: 'keyword', @@ -274,6 +289,58 @@ describe('mappingFromFieldMap', () => { }, }, }); + expect(mappingFromFieldMap(legacyAlertFieldMap)).toEqual({ + dynamic: 'strict', + properties: { + kibana: { + properties: { + alert: { + properties: { + risk_score: { type: 'float' }, + rule: { + properties: { + author: { type: 'keyword' }, + created_at: { type: 'date' }, + created_by: { type: 'keyword' }, + description: { type: 'keyword' }, + enabled: { type: 'keyword' }, + from: { type: 'keyword' }, + interval: { type: 'keyword' }, + license: { type: 'keyword' }, + note: { type: 'keyword' }, + references: { type: 'keyword' }, + rule_id: { type: 'keyword' }, + rule_name_override: { type: 'keyword' }, + to: { type: 'keyword' }, + type: { type: 'keyword' }, + updated_at: { type: 'date' }, + updated_by: { type: 'keyword' }, + version: { type: 'keyword' }, + }, + }, + severity: { type: 'keyword' }, + suppression: { + properties: { + docs_count: { type: 'long' }, + end: { type: 'date' }, + terms: { + properties: { field: { type: 'keyword' }, value: { type: 'keyword' } }, + }, + start: { type: 'date' }, + }, + }, + system_status: { type: 'keyword' }, + workflow_reason: { type: 'keyword' }, + workflow_user: { type: 'keyword' }, + }, + }, + }, + }, + ecs: { properties: { version: { type: 'keyword' } } }, + event: { properties: { action: { type: 'keyword' }, kind: { type: 'keyword' } } }, + tags: { type: 'keyword' }, + }, + }); }); it('uses dynamic setting if specified', () => { diff --git a/x-pack/plugins/alerting/common/alert_schema/field_maps/mapping_from_field_map.ts b/x-pack/plugins/alerting/common/alert_schema/field_maps/mapping_from_field_map.ts index 5a1de7a995b36..9d1db8e577aa5 100644 --- a/x-pack/plugins/alerting/common/alert_schema/field_maps/mapping_from_field_map.ts +++ b/x-pack/plugins/alerting/common/alert_schema/field_maps/mapping_from_field_map.ts @@ -7,7 +7,7 @@ import type { MappingTypeMapping } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { set } from '@kbn/safer-lodash-set'; -import { FieldMap, MultiField } from './types'; +import type { FieldMap, MultiField } from '@kbn/alerts-as-data-utils'; export function mappingFromFieldMap( fieldMap: FieldMap, @@ -29,7 +29,6 @@ export function mappingFromFieldMap( fields.forEach((field) => { // eslint-disable-next-line @typescript-eslint/naming-convention const { name, required, array, multi_fields, ...rest } = field; - const mapped = multi_fields ? { ...rest, diff --git a/x-pack/plugins/alerting/common/alert_schema/field_maps/types.ts b/x-pack/plugins/alerting/common/alert_schema/field_maps/types.ts deleted file mode 100644 index b687cbfb0cf7d..0000000000000 --- a/x-pack/plugins/alerting/common/alert_schema/field_maps/types.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export interface MultiField { - flat_name?: string; - name: string; - type: string; -} - -export interface FieldMap { - [key: string]: { - type: string; - required: boolean; - array?: boolean; - doc_values?: boolean; - enabled?: boolean; - format?: string; - ignore_above?: number; - index?: boolean; - multi_fields?: MultiField[]; - path?: string; - scaling_factor?: number; - dynamic?: boolean | string; - }; -} diff --git a/x-pack/plugins/alerting/common/alert_schema/index.ts b/x-pack/plugins/alerting/common/alert_schema/index.ts index acca43450fe34..cccb492b10e17 100644 --- a/x-pack/plugins/alerting/common/alert_schema/index.ts +++ b/x-pack/plugins/alerting/common/alert_schema/index.ts @@ -5,5 +5,5 @@ * 2.0. */ -export { alertFieldMap } from './field_maps/alert_field_map'; +export { mappingFromFieldMap } from './field_maps/mapping_from_field_map'; export { getComponentTemplateFromFieldMap } from './field_maps/component_template_from_field_map'; diff --git a/x-pack/plugins/alerting/common/index.ts b/x-pack/plugins/alerting/common/index.ts index fafadcdb99ee4..64e5a78d21e8d 100644 --- a/x-pack/plugins/alerting/common/index.ts +++ b/x-pack/plugins/alerting/common/index.ts @@ -24,6 +24,8 @@ export * from './parse_duration'; export * from './execution_log_types'; export * from './rule_snooze_type'; +export { mappingFromFieldMap, getComponentTemplateFromFieldMap } from './alert_schema'; + export interface AlertingFrameworkHealth { isSufficientlySecure: boolean; hasPermanentEncryptionKey: boolean; diff --git a/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.test.ts b/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.test.ts index e65d4e477e87f..5da807ddba65d 100644 --- a/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.test.ts @@ -6,7 +6,7 @@ */ import { loggingSystemMock } from '@kbn/core/server/mocks'; import { UntypedNormalizedRuleType } from '../rule_type_registry'; -import { AlertInstanceContext, RecoveredActionGroup } from '../types'; +import { AlertInstanceContext, RecoveredActionGroup, RuleNotifyWhen } from '../types'; import { LegacyAlertsClient } from './legacy_alerts_client'; import { createAlertFactory, getPublicAlertFactory } from '../alert/create_alert_factory'; import { Alert } from '../alert/alert'; @@ -208,6 +208,10 @@ describe('Legacy Alerts Client', () => { '1': new Alert('1', testAlert1), '2': new Alert('2', testAlert2), }, + currentActiveAlerts: { + '1': new Alert('1', testAlert1), + '2': new Alert('2', testAlert2), + }, currentRecoveredAlerts: {}, recoveredAlerts: {}, }); @@ -231,6 +235,7 @@ describe('Legacy Alerts Client', () => { ruleRunMetricsStore, shouldLogAndScheduleActionsForAlerts: true, flappingSettings: DEFAULT_FLAPPING_SETTINGS, + notifyWhen: RuleNotifyWhen.CHANGE, }); expect(processAlerts).toHaveBeenCalledWith({ @@ -268,6 +273,7 @@ describe('Legacy Alerts Client', () => { lookBackWindow: 20, statusChangeThreshold: 4, }, + RuleNotifyWhen.CHANGE, 'default', {}, { diff --git a/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.ts b/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.ts index e935fcd9938de..9affe3a67d7eb 100644 --- a/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.ts +++ b/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.ts @@ -28,6 +28,7 @@ import { AlertInstanceState, RawAlertInstance, WithoutReservedActionGroups, + RuleNotifyWhenType, } from '../types'; import { RulesSettingsFlappingProperties } from '../../common/rules_settings'; @@ -49,6 +50,7 @@ export class LegacyAlertsClient< private processedAlerts: { new: Record>; active: Record>; + activeCurrent: Record>; recovered: Record>; recoveredCurrent: Record>; }; @@ -65,6 +67,7 @@ export class LegacyAlertsClient< this.processedAlerts = { new: {}, active: {}, + activeCurrent: {}, recovered: {}, recoveredCurrent: {}, }; @@ -113,12 +116,14 @@ export class LegacyAlertsClient< ruleRunMetricsStore, shouldLogAndScheduleActionsForAlerts, flappingSettings, + notifyWhen, }: { eventLogger: AlertingEventLogger; ruleLabel: string; shouldLogAndScheduleActionsForAlerts: boolean; ruleRunMetricsStore: RuleRunMetricsStore; flappingSettings: RulesSettingsFlappingProperties; + notifyWhen: RuleNotifyWhenType | null; }) { const { newAlerts: processedAlertsNew, @@ -152,6 +157,7 @@ export class LegacyAlertsClient< const alerts = getAlertsForNotification( flappingSettings, + notifyWhen, this.options.ruleType.defaultActionGroupId, processedAlertsNew, processedAlertsActive, @@ -162,6 +168,7 @@ export class LegacyAlertsClient< this.processedAlerts.new = alerts.newAlerts; this.processedAlerts.active = alerts.activeAlerts; + this.processedAlerts.activeCurrent = alerts.currentActiveAlerts; this.processedAlerts.recovered = alerts.recoveredAlerts; this.processedAlerts.recoveredCurrent = alerts.currentRecoveredAlerts; @@ -169,7 +176,7 @@ export class LegacyAlertsClient< logger: this.options.logger, alertingEventLogger: eventLogger, newAlerts: alerts.newAlerts, - activeAlerts: alerts.activeAlerts, + activeAlerts: alerts.currentActiveAlerts, recoveredAlerts: alerts.currentRecoveredAlerts, ruleLogPrefix: ruleLabel, ruleRunMetricsStore, @@ -178,7 +185,9 @@ export class LegacyAlertsClient< }); } - public getProcessedAlerts(type: 'new' | 'active' | 'recovered' | 'recoveredCurrent') { + public getProcessedAlerts( + type: 'new' | 'active' | 'activeCurrent' | 'recovered' | 'recoveredCurrent' + ) { if (this.processedAlerts.hasOwnProperty(type)) { return this.processedAlerts[type]; } diff --git a/x-pack/plugins/alerting/server/alerts_service/alerts_service.test.ts b/x-pack/plugins/alerting/server/alerts_service/alerts_service.test.ts index 82716f935c6b3..ca64faa7c51ea 100644 --- a/x-pack/plugins/alerting/server/alerts_service/alerts_service.test.ts +++ b/x-pack/plugins/alerting/server/alerts_service/alerts_service.test.ts @@ -6,9 +6,11 @@ */ import { elasticsearchServiceMock, loggingSystemMock } from '@kbn/core/server/mocks'; +import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; import { errors as EsErrors } from '@elastic/elasticsearch'; import { ReplaySubject, Subject } from 'rxjs'; import { AlertsService } from './alerts_service'; +import { IRuleTypeAlerts } from '../types'; let logger: ReturnType; const clusterClient = elasticsearchServiceMock.createClusterClient().asInternalUser; @@ -75,40 +77,52 @@ const IlmPutBody = { name: '.alerts-ilm-policy', }; -const getIndexTemplatePutBody = (context?: string) => ({ - name: `.alerts-${context ? context : 'test'}-default-template`, - body: { - index_patterns: [`.alerts-${context ? context : 'test'}-default-*`], - composed_of: [ - 'alerts-common-component-template', - `alerts-${context ? context : 'test'}-component-template`, - ], - template: { - settings: { - auto_expand_replicas: '0-1', - hidden: true, - 'index.lifecycle': { - name: '.alerts-ilm-policy', - rollover_alias: `.alerts-${context ? context : 'test'}-default`, +interface GetIndexTemplatePutBodyOpts { + context?: string; + useLegacyAlerts?: boolean; + useEcs?: boolean; +} +const getIndexTemplatePutBody = (opts?: GetIndexTemplatePutBodyOpts) => { + const context = opts ? opts.context : undefined; + const useLegacyAlerts = opts ? opts.useLegacyAlerts : undefined; + const useEcs = opts ? opts.useEcs : undefined; + return { + name: `.alerts-${context ? context : 'test'}-default-template`, + body: { + index_patterns: [`.alerts-${context ? context : 'test'}-default-*`], + composed_of: [ + `.alerts-${context ? context : 'test'}-mappings`, + ...(useLegacyAlerts ? ['.alerts-legacy-alert-mappings'] : []), + ...(useEcs ? ['.alerts-ecs-mappings'] : []), + '.alerts-framework-mappings', + ], + template: { + settings: { + auto_expand_replicas: '0-1', + hidden: true, + 'index.lifecycle': { + name: '.alerts-ilm-policy', + rollover_alias: `.alerts-${context ? context : 'test'}-default`, + }, + 'index.mapping.total_fields.limit': 2500, + }, + mappings: { + dynamic: false, }, - 'index.mapping.total_fields.limit': 2500, }, - mappings: { - dynamic: false, + _meta: { + managed: true, }, }, - _meta: { - managed: true, - }, - }, -}); + }; +}; -const TestRegistrationContext = { +const TestRegistrationContext: IRuleTypeAlerts = { context: 'test', fieldMap: { field: { type: 'keyword', required: false } }, }; -const AnotherRegistrationContext = { +const AnotherRegistrationContext: IRuleTypeAlerts = { context: 'another', fieldMap: { field: { type: 'keyword', required: false } }, }; @@ -145,10 +159,14 @@ describe('Alerts Service', () => { expect(alertsService.isInitialized()).toEqual(true); expect(clusterClient.ilm.putLifecycle).toHaveBeenCalledWith(IlmPutBody); - expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(1); + expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(3); const componentTemplate1 = clusterClient.cluster.putComponentTemplate.mock.calls[0][0]; - expect(componentTemplate1.name).toEqual('alerts-common-component-template'); + expect(componentTemplate1.name).toEqual('.alerts-framework-mappings'); + const componentTemplate2 = clusterClient.cluster.putComponentTemplate.mock.calls[1][0]; + expect(componentTemplate2.name).toEqual('.alerts-legacy-alert-mappings'); + const componentTemplate3 = clusterClient.cluster.putComponentTemplate.mock.calls[2][0]; + expect(componentTemplate3.name).toEqual('.alerts-ecs-mappings'); }); test('should log error and set initialized to false if adding ILM policy throws error', async () => { @@ -185,13 +203,105 @@ describe('Alerts Service', () => { expect(alertsService.isInitialized()).toEqual(false); expect(logger.error).toHaveBeenCalledWith( - `Error installing component template alerts-common-component-template - fail` + `Error installing component template .alerts-framework-mappings - fail` ); expect(clusterClient.ilm.putLifecycle).toHaveBeenCalled(); expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(1); }); + test('should update index template field limit and retry initialization if creating/updating common component template fails with field limit error', async () => { + clusterClient.cluster.putComponentTemplate.mockRejectedValueOnce( + new EsErrors.ResponseError( + elasticsearchClientMock.createApiResponse({ + statusCode: 400, + body: { + error: { + root_cause: [ + { + type: 'illegal_argument_exception', + reason: + 'updating component template [.alerts-ecs-mappings] results in invalid composable template [.alerts-security.alerts-default-index-template] after templates are merged', + }, + ], + type: 'illegal_argument_exception', + reason: + 'updating component template [.alerts-ecs-mappings] results in invalid composable template [.alerts-security.alerts-default-index-template] after templates are merged', + caused_by: { + type: 'illegal_argument_exception', + reason: + 'composable template [.alerts-security.alerts-default-index-template] template after composition with component templates [.alerts-ecs-mappings, .alerts-security.alerts-mappings, .alerts-technical-mappings] is invalid', + caused_by: { + type: 'illegal_argument_exception', + reason: + 'invalid composite mappings for [.alerts-security.alerts-default-index-template]', + caused_by: { + type: 'illegal_argument_exception', + reason: 'Limit of total fields [1900] has been exceeded', + }, + }, + }, + }, + }, + }) + ) + ); + const existingIndexTemplate = { + name: 'test-template', + index_template: { + index_patterns: ['test*'], + composed_of: ['.alerts-framework-mappings'], + template: { + settings: { + auto_expand_replicas: '0-1', + hidden: true, + 'index.lifecycle': { + name: '.alerts-ilm-policy', + rollover_alias: `.alerts-empty-default`, + }, + 'index.mapping.total_fields.limit': 1800, + }, + mappings: { + dynamic: false, + }, + }, + }, + }; + clusterClient.indices.getIndexTemplate.mockResolvedValueOnce({ + index_templates: [existingIndexTemplate], + }); + const alertsService = new AlertsService({ + logger, + elasticsearchClientPromise: Promise.resolve(clusterClient), + pluginStop$, + }); + + alertsService.initialize(); + await new Promise((r) => setTimeout(r, 50)); + + expect(alertsService.isInitialized()).toEqual(true); + expect(clusterClient.indices.getIndexTemplate).toHaveBeenCalledTimes(1); + expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalledTimes(1); + expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalledWith({ + name: existingIndexTemplate.name, + body: { + ...existingIndexTemplate.index_template, + template: { + ...existingIndexTemplate.index_template.template, + settings: { + ...existingIndexTemplate.index_template.template?.settings, + 'index.mapping.total_fields.limit': 2500, + }, + }, + }, + }); + + expect(clusterClient.ilm.putLifecycle).toHaveBeenCalled(); + // 3x for framework, legacy-alert and ecs mappings, then 1 extra time to update component template + // after updating index template field limit + expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(4); + }); + test('should install resources for contexts awaiting initialization when common resources are initialized', async () => { const alertsService = new AlertsService({ logger, @@ -214,20 +324,24 @@ describe('Alerts Service', () => { ); expect(clusterClient.ilm.putLifecycle).toHaveBeenCalledWith(IlmPutBody); - // 1x for common component template, 2x for context specific - expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(3); + // 1x for framework component template, 1x for legacy alert, 1x for ecs, 2x for context specific + expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(5); const componentTemplate1 = clusterClient.cluster.putComponentTemplate.mock.calls[0][0]; - expect(componentTemplate1.name).toEqual('alerts-common-component-template'); + expect(componentTemplate1.name).toEqual('.alerts-framework-mappings'); const componentTemplate2 = clusterClient.cluster.putComponentTemplate.mock.calls[1][0]; - expect(componentTemplate2.name).toEqual('alerts-another-component-template'); + expect(componentTemplate2.name).toEqual('.alerts-legacy-alert-mappings'); const componentTemplate3 = clusterClient.cluster.putComponentTemplate.mock.calls[2][0]; - expect(componentTemplate3.name).toEqual('alerts-test-component-template'); + expect(componentTemplate3.name).toEqual('.alerts-ecs-mappings'); + const componentTemplate4 = clusterClient.cluster.putComponentTemplate.mock.calls[3][0]; + expect(componentTemplate4.name).toEqual('.alerts-another-mappings'); + const componentTemplate5 = clusterClient.cluster.putComponentTemplate.mock.calls[4][0]; + expect(componentTemplate5.name).toEqual('.alerts-test-mappings'); expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalledTimes(2); expect(clusterClient.indices.putIndexTemplate).toHaveBeenNthCalledWith( 1, - getIndexTemplatePutBody('another') + getIndexTemplatePutBody({ context: 'another' }) ); expect(clusterClient.indices.putIndexTemplate).toHaveBeenNthCalledWith( 2, @@ -291,11 +405,15 @@ describe('Alerts Service', () => { expect(clusterClient.ilm.putLifecycle).toHaveBeenCalledWith(IlmPutBody); - expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(2); + expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(4); const componentTemplate1 = clusterClient.cluster.putComponentTemplate.mock.calls[0][0]; - expect(componentTemplate1.name).toEqual('alerts-common-component-template'); + expect(componentTemplate1.name).toEqual('.alerts-framework-mappings'); const componentTemplate2 = clusterClient.cluster.putComponentTemplate.mock.calls[1][0]; - expect(componentTemplate2.name).toEqual('alerts-test-component-template'); + expect(componentTemplate2.name).toEqual('.alerts-legacy-alert-mappings'); + const componentTemplate3 = clusterClient.cluster.putComponentTemplate.mock.calls[2][0]; + expect(componentTemplate3.name).toEqual('.alerts-ecs-mappings'); + const componentTemplate4 = clusterClient.cluster.putComponentTemplate.mock.calls[3][0]; + expect(componentTemplate4.name).toEqual('.alerts-test-mappings'); expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalledWith( getIndexTemplatePutBody() @@ -318,7 +436,87 @@ describe('Alerts Service', () => { }); }); - test('should not install component template for context fieldMap is empty', async () => { + test('should correctly install resources for context when useLegacyAlerts is true', async () => { + alertsService.register({ ...TestRegistrationContext, useLegacyAlerts: true }); + await new Promise((r) => setTimeout(r, 50)); + expect(await alertsService.isContextInitialized(TestRegistrationContext.context)).toEqual( + true + ); + + expect(clusterClient.ilm.putLifecycle).toHaveBeenCalledWith(IlmPutBody); + + expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(4); + const componentTemplate1 = clusterClient.cluster.putComponentTemplate.mock.calls[0][0]; + expect(componentTemplate1.name).toEqual('.alerts-framework-mappings'); + const componentTemplate2 = clusterClient.cluster.putComponentTemplate.mock.calls[1][0]; + expect(componentTemplate2.name).toEqual('.alerts-legacy-alert-mappings'); + const componentTemplate3 = clusterClient.cluster.putComponentTemplate.mock.calls[2][0]; + expect(componentTemplate3.name).toEqual('.alerts-ecs-mappings'); + const componentTemplate4 = clusterClient.cluster.putComponentTemplate.mock.calls[3][0]; + expect(componentTemplate4.name).toEqual('.alerts-test-mappings'); + + expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalledWith( + getIndexTemplatePutBody({ useLegacyAlerts: true }) + ); + expect(clusterClient.indices.getAlias).toHaveBeenCalledWith({ + index: '.alerts-test-default-*', + }); + expect(clusterClient.indices.putSettings).toHaveBeenCalledTimes(2); + expect(clusterClient.indices.simulateIndexTemplate).toHaveBeenCalledTimes(2); + expect(clusterClient.indices.putMapping).toHaveBeenCalledTimes(2); + expect(clusterClient.indices.create).toHaveBeenCalledWith({ + index: '.alerts-test-default-000001', + body: { + aliases: { + '.alerts-test-default': { + is_write_index: true, + }, + }, + }, + }); + }); + + test('should correctly install resources for context when useEcs is true', async () => { + alertsService.register({ ...TestRegistrationContext, useEcs: true }); + await new Promise((r) => setTimeout(r, 50)); + expect(await alertsService.isContextInitialized(TestRegistrationContext.context)).toEqual( + true + ); + + expect(clusterClient.ilm.putLifecycle).toHaveBeenCalledWith(IlmPutBody); + + expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(4); + const componentTemplate1 = clusterClient.cluster.putComponentTemplate.mock.calls[0][0]; + expect(componentTemplate1.name).toEqual('.alerts-framework-mappings'); + const componentTemplate2 = clusterClient.cluster.putComponentTemplate.mock.calls[1][0]; + expect(componentTemplate2.name).toEqual('.alerts-legacy-alert-mappings'); + const componentTemplate3 = clusterClient.cluster.putComponentTemplate.mock.calls[2][0]; + expect(componentTemplate3.name).toEqual('.alerts-ecs-mappings'); + const componentTemplate4 = clusterClient.cluster.putComponentTemplate.mock.calls[3][0]; + expect(componentTemplate4.name).toEqual('.alerts-test-mappings'); + + expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalledWith( + getIndexTemplatePutBody({ useEcs: true }) + ); + expect(clusterClient.indices.getAlias).toHaveBeenCalledWith({ + index: '.alerts-test-default-*', + }); + expect(clusterClient.indices.putSettings).toHaveBeenCalledTimes(2); + expect(clusterClient.indices.simulateIndexTemplate).toHaveBeenCalledTimes(2); + expect(clusterClient.indices.putMapping).toHaveBeenCalledTimes(2); + expect(clusterClient.indices.create).toHaveBeenCalledWith({ + index: '.alerts-test-default-000001', + body: { + aliases: { + '.alerts-test-default': { + is_write_index: true, + }, + }, + }, + }); + }); + + test('should not install component template for context if fieldMap is empty', async () => { alertsService.register({ context: 'empty', fieldMap: {}, @@ -328,15 +526,19 @@ describe('Alerts Service', () => { expect(clusterClient.ilm.putLifecycle).toHaveBeenCalledWith(IlmPutBody); - expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(1); + expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(3); const componentTemplate1 = clusterClient.cluster.putComponentTemplate.mock.calls[0][0]; - expect(componentTemplate1.name).toEqual('alerts-common-component-template'); + expect(componentTemplate1.name).toEqual('.alerts-framework-mappings'); + const componentTemplate2 = clusterClient.cluster.putComponentTemplate.mock.calls[1][0]; + expect(componentTemplate2.name).toEqual('.alerts-legacy-alert-mappings'); + const componentTemplate3 = clusterClient.cluster.putComponentTemplate.mock.calls[2][0]; + expect(componentTemplate3.name).toEqual('.alerts-ecs-mappings'); expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalledWith({ name: `.alerts-empty-default-template`, body: { index_patterns: [`.alerts-empty-default-*`], - composed_of: ['alerts-common-component-template'], + composed_of: ['.alerts-framework-mappings'], template: { settings: { auto_expand_replicas: '0-1', @@ -410,7 +612,7 @@ describe('Alerts Service', () => { ); expect(clusterClient.ilm.putLifecycle).toHaveBeenCalled(); - expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(2); + expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(4); expect(clusterClient.indices.simulateTemplate).toHaveBeenCalled(); // putIndexTemplate is skipped but other operations are called as expected expect(clusterClient.indices.putIndexTemplate).not.toHaveBeenCalled(); @@ -443,7 +645,7 @@ describe('Alerts Service', () => { ); expect(clusterClient.ilm.putLifecycle).toHaveBeenCalled(); - expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(2); + expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(4); expect(clusterClient.indices.simulateTemplate).toHaveBeenCalled(); expect(clusterClient.indices.putIndexTemplate).not.toHaveBeenCalled(); expect(clusterClient.indices.getAlias).not.toHaveBeenCalled(); @@ -467,7 +669,7 @@ describe('Alerts Service', () => { ); expect(clusterClient.ilm.putLifecycle).toHaveBeenCalled(); - expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(2); + expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(4); expect(clusterClient.indices.simulateTemplate).toHaveBeenCalled(); expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalled(); expect(clusterClient.indices.getAlias).not.toHaveBeenCalled(); @@ -491,7 +693,7 @@ describe('Alerts Service', () => { ); expect(clusterClient.ilm.putLifecycle).toHaveBeenCalled(); - expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(2); + expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(4); expect(clusterClient.indices.simulateTemplate).toHaveBeenCalled(); expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalled(); expect(clusterClient.indices.putSettings).not.toHaveBeenCalled(); @@ -512,7 +714,7 @@ describe('Alerts Service', () => { ); expect(clusterClient.ilm.putLifecycle).toHaveBeenCalled(); - expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(2); + expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(4); expect(clusterClient.indices.simulateTemplate).toHaveBeenCalled(); expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalled(); expect(clusterClient.indices.putSettings).not.toHaveBeenCalled(); @@ -535,7 +737,7 @@ describe('Alerts Service', () => { ); expect(clusterClient.ilm.putLifecycle).toHaveBeenCalled(); - expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(2); + expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(4); expect(clusterClient.indices.simulateTemplate).toHaveBeenCalled(); expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalled(); expect(clusterClient.indices.getAlias).toHaveBeenCalled(); @@ -559,7 +761,7 @@ describe('Alerts Service', () => { ); expect(clusterClient.ilm.putLifecycle).toHaveBeenCalled(); - expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(2); + expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(4); expect(clusterClient.indices.simulateTemplate).toHaveBeenCalled(); expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalled(); expect(clusterClient.indices.getAlias).toHaveBeenCalled(); @@ -581,7 +783,7 @@ describe('Alerts Service', () => { expect(logger.error).toHaveBeenCalledWith(`Failed to PUT mapping for alias alias_1: fail`); expect(clusterClient.ilm.putLifecycle).toHaveBeenCalled(); - expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(2); + expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(4); expect(clusterClient.indices.simulateTemplate).toHaveBeenCalled(); expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalled(); expect(clusterClient.indices.getAlias).toHaveBeenCalled(); @@ -601,7 +803,7 @@ describe('Alerts Service', () => { ); expect(clusterClient.ilm.putLifecycle).toHaveBeenCalled(); - expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(2); + expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(4); expect(clusterClient.indices.simulateTemplate).toHaveBeenCalled(); expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalled(); expect(clusterClient.indices.getAlias).toHaveBeenCalled(); @@ -640,7 +842,7 @@ describe('Alerts Service', () => { ); expect(clusterClient.ilm.putLifecycle).toHaveBeenCalled(); - expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(2); + expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(4); expect(clusterClient.indices.simulateTemplate).toHaveBeenCalled(); expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalled(); expect(clusterClient.indices.getAlias).toHaveBeenCalled(); @@ -673,7 +875,7 @@ describe('Alerts Service', () => { ); expect(clusterClient.ilm.putLifecycle).toHaveBeenCalled(); - expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(2); + expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(4); expect(clusterClient.indices.simulateTemplate).toHaveBeenCalled(); expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalled(); expect(clusterClient.indices.getAlias).toHaveBeenCalled(); @@ -695,7 +897,7 @@ describe('Alerts Service', () => { expect(logger.error).toHaveBeenCalledWith(`Error creating concrete write index - fail`); expect(clusterClient.ilm.putLifecycle).toHaveBeenCalled(); - expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(2); + expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(4); expect(clusterClient.indices.simulateTemplate).toHaveBeenCalled(); expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalled(); expect(clusterClient.indices.getAlias).toHaveBeenCalled(); @@ -730,7 +932,7 @@ describe('Alerts Service', () => { expect(logger.error).toHaveBeenCalledWith(`Error creating concrete write index - fail`); expect(clusterClient.ilm.putLifecycle).toHaveBeenCalled(); - expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(2); + expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(4); expect(clusterClient.indices.simulateTemplate).toHaveBeenCalled(); expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalled(); expect(clusterClient.indices.getAlias).toHaveBeenCalled(); @@ -766,7 +968,7 @@ describe('Alerts Service', () => { expect(logger.error).toHaveBeenCalledWith(`Error creating concrete write index - fail`); expect(clusterClient.ilm.putLifecycle).toHaveBeenCalled(); - expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(2); + expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(4); expect(clusterClient.indices.simulateTemplate).toHaveBeenCalled(); expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalled(); expect(clusterClient.indices.getAlias).toHaveBeenCalled(); @@ -810,7 +1012,7 @@ describe('Alerts Service', () => { alertsService.initialize(); await new Promise((r) => setTimeout(r, 150)); expect(alertsService.isInitialized()).toEqual(true); - expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(3); + expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(5); }); test('should retry updating index template for transient ES errors', async () => { diff --git a/x-pack/plugins/alerting/server/alerts_service/alerts_service.ts b/x-pack/plugins/alerting/server/alerts_service/alerts_service.ts index 08643caf862f5..22cb9f4df1884 100644 --- a/x-pack/plugins/alerting/server/alerts_service/alerts_service.ts +++ b/x-pack/plugins/alerting/server/alerts_service/alerts_service.ts @@ -13,8 +13,14 @@ import { import { get, isEmpty, isEqual } from 'lodash'; import { Logger, ElasticsearchClient } from '@kbn/core/server'; import { firstValueFrom, Observable } from 'rxjs'; -import { FieldMap } from '../../common/alert_schema/field_maps/types'; -import { alertFieldMap } from '../../common/alert_schema'; +import { + alertFieldMap, + ecsFieldMap, + legacyAlertFieldMap, + type FieldMap, +} from '@kbn/alerts-as-data-utils'; +import { IndicesGetIndexTemplateIndexTemplateItem } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { asyncForEach } from '@kbn/std'; import { DEFAULT_ALERTS_ILM_POLICY_NAME, DEFAULT_ALERTS_ILM_POLICY, @@ -34,7 +40,9 @@ import { const TOTAL_FIELDS_LIMIT = 2500; const INSTALLATION_TIMEOUT = 20 * 60 * 1000; // 20 minutes - +const LEGACY_ALERT_CONTEXT = 'legacy-alert'; +export const ECS_CONTEXT = `ecs`; +export const ECS_COMPONENT_TEMPLATE_NAME = getComponentTemplateName(ECS_CONTEXT); interface AlertsServiceParams { logger: Logger; pluginStop$: Observable; @@ -107,6 +115,16 @@ export class AlertsService implements IAlertsService { const initFns = [ () => this.createOrUpdateIlmPolicy(esClient), () => this.createOrUpdateComponentTemplate(esClient, getComponentTemplate(alertFieldMap)), + () => + this.createOrUpdateComponentTemplate( + esClient, + getComponentTemplate(legacyAlertFieldMap, LEGACY_ALERT_CONTEXT) + ), + () => + this.createOrUpdateComponentTemplate( + esClient, + getComponentTemplate(ecsFieldMap, ECS_CONTEXT) + ), ]; for (const fn of initFns) { @@ -127,7 +145,8 @@ export class AlertsService implements IAlertsService { }); } - public register({ context, fieldMap }: IRuleTypeAlerts, timeoutMs?: number) { + public register(opts: IRuleTypeAlerts, timeoutMs?: number) { + const { context, fieldMap } = opts; // check whether this context has been registered before if (this.registeredContexts.has(context)) { const registeredFieldMap = this.registeredContexts.get(context); @@ -140,37 +159,54 @@ export class AlertsService implements IAlertsService { this.options.logger.info(`Registering resources for context "${context}".`); this.registeredContexts.set(context, fieldMap); - this.resourceInitializationHelper.add({ context, fieldMap }, timeoutMs); + this.resourceInitializationHelper.add(opts, timeoutMs); } - private async initializeContext({ context, fieldMap }: IRuleTypeAlerts, timeoutMs?: number) { + private async initializeContext( + { context, fieldMap, useEcs, useLegacyAlerts }: IRuleTypeAlerts, + timeoutMs?: number + ) { const esClient = await this.options.elasticsearchClientPromise; const indexTemplateAndPattern = getIndexTemplateAndPattern(context); - // Context specific initialization installs component template, index template and write index - // If fieldMap is empty, don't create context specific component template - const initFns = isEmpty(fieldMap) - ? [ - async () => - await this.createOrUpdateIndexTemplate(esClient, indexTemplateAndPattern, [ - getComponentTemplateName(), - ]), - async () => await this.createConcreteWriteIndex(esClient, indexTemplateAndPattern), - ] - : [ - async () => - await this.createOrUpdateComponentTemplate( - esClient, - getComponentTemplate(fieldMap, context) - ), - async () => - await this.createOrUpdateIndexTemplate(esClient, indexTemplateAndPattern, [ - getComponentTemplateName(), - getComponentTemplateName(context), - ]), - async () => await this.createConcreteWriteIndex(esClient, indexTemplateAndPattern), - ]; + let initFns: Array<() => Promise> = []; + + // List of component templates to reference + const componentTemplateRefs: string[] = []; + + // If fieldMap is not empty, create a context specific component template + if (!isEmpty(fieldMap)) { + const componentTemplate = getComponentTemplate(fieldMap, context); + initFns.push( + async () => await this.createOrUpdateComponentTemplate(esClient, componentTemplate) + ); + componentTemplateRefs.push(componentTemplate.name); + } + + // If useLegacy is set to true, add the legacy alert component template to the references + if (useLegacyAlerts) { + componentTemplateRefs.push(getComponentTemplateName(LEGACY_ALERT_CONTEXT)); + } + + // If useEcs is set to true, add the ECS component template to the references + if (useEcs) { + componentTemplateRefs.push(getComponentTemplateName(ECS_CONTEXT)); + } + + // Add framework component template to the references + componentTemplateRefs.push(getComponentTemplateName()); + + // Context specific initialization installs index template and write index + initFns = initFns.concat([ + async () => + await this.createOrUpdateIndexTemplate( + esClient, + indexTemplateAndPattern, + componentTemplateRefs + ), + async () => await this.createConcreteWriteIndex(esClient, indexTemplateAndPattern), + ]); for (const fn of initFns) { await this.installWithTimeout(async () => await fn(), timeoutMs); @@ -200,6 +236,62 @@ export class AlertsService implements IAlertsService { } } + private async getIndexTemplatesUsingComponentTemplate( + esClient: ElasticsearchClient, + componentTemplateName: string + ) { + // Get all index templates and filter down to just the ones referencing this component template + const { index_templates: indexTemplates } = await esClient.indices.getIndexTemplate(); + const indexTemplatesUsingComponentTemplate = (indexTemplates ?? []).filter( + (indexTemplate: IndicesGetIndexTemplateIndexTemplateItem) => + indexTemplate.index_template.composed_of.includes(componentTemplateName) + ); + await asyncForEach( + indexTemplatesUsingComponentTemplate, + async (template: IndicesGetIndexTemplateIndexTemplateItem) => { + await esClient.indices.putIndexTemplate({ + name: template.name, + body: { + ...template.index_template, + template: { + ...template.index_template.template, + settings: { + ...template.index_template.template?.settings, + 'index.mapping.total_fields.limit': TOTAL_FIELDS_LIMIT, + }, + }, + }, + }); + } + ); + } + + private async createOrUpdateComponentTemplateHelper( + esClient: ElasticsearchClient, + template: ClusterPutComponentTemplateRequest + ) { + try { + await esClient.cluster.putComponentTemplate(template); + } catch (error) { + const reason = error?.meta?.body?.error?.caused_by?.caused_by?.caused_by?.reason; + if (reason && reason.match(/Limit of total fields \[\d+\] has been exceeded/) != null) { + // This error message occurs when there is an index template using this component template + // that contains a field limit setting that using this component template exceeds + // Specifically, this can happen for the ECS component template when we add new fields + // to adhere to the ECS spec. Individual index templates specify field limits so if the + // number of new ECS fields pushes the composed mapping above the limit, this error will + // occur. We have to update the field limit inside the index template now otherwise we + // can never update the component template + await this.getIndexTemplatesUsingComponentTemplate(esClient, template.name); + + // Try to update the component template again + await esClient.cluster.putComponentTemplate(template); + } else { + throw error; + } + } + } + private async createOrUpdateComponentTemplate( esClient: ElasticsearchClient, template: ClusterPutComponentTemplateRequest @@ -207,9 +299,12 @@ export class AlertsService implements IAlertsService { this.options.logger.info(`Installing component template ${template.name}`); try { - await retryTransientEsErrors(() => esClient.cluster.putComponentTemplate(template), { - logger: this.options.logger, - }); + await retryTransientEsErrors( + () => this.createOrUpdateComponentTemplateHelper(esClient, template), + { + logger: this.options.logger, + } + ); } catch (err) { this.options.logger.error( `Error installing component template ${template.name} - ${err.message}` diff --git a/x-pack/plugins/alerting/server/alerts_service/index.ts b/x-pack/plugins/alerting/server/alerts_service/index.ts new file mode 100644 index 0000000000000..49247f3baa243 --- /dev/null +++ b/x-pack/plugins/alerting/server/alerts_service/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. + */ + +export { + DEFAULT_ALERTS_ILM_POLICY, + DEFAULT_ALERTS_ILM_POLICY_NAME, +} from './default_lifecycle_policy'; +export { ECS_COMPONENT_TEMPLATE_NAME, ECS_CONTEXT } from './alerts_service'; +export { getComponentTemplate } from './types'; diff --git a/x-pack/plugins/alerting/server/alerts_service/types.ts b/x-pack/plugins/alerting/server/alerts_service/types.ts index db47a9a8e0015..aeb73cab6ffd2 100644 --- a/x-pack/plugins/alerting/server/alerts_service/types.ts +++ b/x-pack/plugins/alerting/server/alerts_service/types.ts @@ -6,11 +6,11 @@ */ import { ClusterPutComponentTemplateRequest } from '@elastic/elasticsearch/lib/api/types'; -import { getComponentTemplateFromFieldMap } from '../../common/alert_schema'; -import { FieldMap } from '../../common/alert_schema/field_maps/types'; +import type { FieldMap } from '@kbn/alerts-as-data-utils'; +import { getComponentTemplateFromFieldMap } from '../../common'; export const getComponentTemplateName = (context?: string) => - `alerts-${context ? context : 'common'}-component-template`; + `.alerts-${context || 'framework'}-mappings`; export interface IIndexPatternString { template: string; @@ -40,5 +40,5 @@ export const getComponentTemplate = ( name: getComponentTemplateName(context), fieldMap, // set field limit slightly higher than actual number of fields - fieldLimit: 100, // Math.round(Object.keys(fieldMap).length * 1.5), + fieldLimit: Math.ceil(Object.keys(fieldMap).length / 1000) * 1000 + 500, }); diff --git a/x-pack/plugins/alerting/server/index.ts b/x-pack/plugins/alerting/server/index.ts index a13b06596f557..6b5ad3012d8a9 100644 --- a/x-pack/plugins/alerting/server/index.ts +++ b/x-pack/plugins/alerting/server/index.ts @@ -56,7 +56,10 @@ export { export { DEFAULT_ALERTS_ILM_POLICY, DEFAULT_ALERTS_ILM_POLICY_NAME, -} from './alerts_service/default_lifecycle_policy'; + ECS_COMPONENT_TEMPLATE_NAME, + ECS_CONTEXT, + getComponentTemplate, +} from './alerts_service'; export const plugin = (initContext: PluginInitializerContext) => new AlertingPlugin(initContext); 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 0c4bf8e04a583..17c636a187edf 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 @@ -8,6 +8,7 @@ import { DEFAULT_FLAPPING_SETTINGS, DISABLE_FLAPPING_SETTINGS } from '../../common/rules_settings'; import { getAlertsForNotification } from '.'; import { Alert } from '../alert'; +import { RuleNotifyWhen } from '../types'; describe('getAlertsForNotification', () => { test('should set pendingRecoveredCount to zero for all active alerts', () => { @@ -16,6 +17,7 @@ describe('getAlertsForNotification', () => { const { newAlerts, activeAlerts } = getAlertsForNotification( DEFAULT_FLAPPING_SETTINGS, + RuleNotifyWhen.CHANGE, 'default', { '1': alert1, @@ -61,28 +63,34 @@ describe('getAlertsForNotification', () => { `); }); - test('should return flapping pending recovered alerts as active alerts', () => { + test('should return flapping pending recovered alerts as active alerts and current active alerts', () => { const alert1 = new Alert('1', { meta: { flapping: true, pendingRecoveredCount: 3 } }); const alert2 = new Alert('2', { meta: { flapping: false } }); const alert3 = new Alert('3', { meta: { flapping: true } }); - const { newAlerts, activeAlerts, recoveredAlerts, currentRecoveredAlerts } = - getAlertsForNotification( - DEFAULT_FLAPPING_SETTINGS, - 'default', - {}, - {}, - { - '1': alert1, - '2': alert2, - '3': alert3, - }, - { - '1': alert1, - '2': alert2, - '3': alert3, - } - ); + const { + newAlerts, + activeAlerts, + currentActiveAlerts, + recoveredAlerts, + currentRecoveredAlerts, + } = getAlertsForNotification( + DEFAULT_FLAPPING_SETTINGS, + RuleNotifyWhen.CHANGE, + 'default', + {}, + {}, + { + '1': alert1, + '2': alert2, + '3': alert3, + }, + { + '1': alert1, + '2': alert2, + '3': alert3, + } + ); expect(newAlerts).toMatchInlineSnapshot(`Object {}`); expect(activeAlerts).toMatchInlineSnapshot(` @@ -107,6 +115,28 @@ describe('getAlertsForNotification', () => { }, ] `); + expect(currentActiveAlerts).toMatchInlineSnapshot(` + Object { + "3": Object { + "meta": Object { + "flapping": true, + "flappingHistory": Array [], + "pendingRecoveredCount": 1, + }, + "state": Object {}, + }, + } + `); + expect(Object.values(currentActiveAlerts).map((a) => a.getScheduledActionOptions())) + .toMatchInlineSnapshot(` + Array [ + Object { + "actionGroup": "default", + "context": Object {}, + "state": Object {}, + }, + ] + `); expect(recoveredAlerts).toMatchInlineSnapshot(` Object { "1": Object { @@ -161,6 +191,7 @@ describe('getAlertsForNotification', () => { const { newAlerts, activeAlerts, recoveredAlerts, currentRecoveredAlerts } = getAlertsForNotification( DISABLE_FLAPPING_SETTINGS, + RuleNotifyWhen.CHANGE, 'default', {}, {}, @@ -259,4 +290,100 @@ describe('getAlertsForNotification', () => { } `); }); + + test('should return flapping pending recovered alerts as active alerts only when notifyWhen is onActionGroupChange', () => { + const alert1 = new Alert('1', { meta: { flapping: true, pendingRecoveredCount: 3 } }); + const alert2 = new Alert('2', { meta: { flapping: false } }); + const alert3 = new Alert('3', { meta: { flapping: true } }); + + const { + newAlerts, + activeAlerts, + currentActiveAlerts, + recoveredAlerts, + currentRecoveredAlerts, + } = getAlertsForNotification( + DEFAULT_FLAPPING_SETTINGS, + RuleNotifyWhen.ACTIVE, + 'default', + {}, + {}, + { + '1': alert1, + '2': alert2, + '3': alert3, + }, + { + '1': alert1, + '2': alert2, + '3': alert3, + } + ); + + expect(newAlerts).toMatchInlineSnapshot(`Object {}`); + expect(activeAlerts).toMatchInlineSnapshot(` + Object { + "3": Object { + "meta": Object { + "flapping": true, + "flappingHistory": Array [], + "pendingRecoveredCount": 1, + }, + "state": Object {}, + }, + } + `); + expect(Object.values(activeAlerts).map((a) => a.getScheduledActionOptions())) + .toMatchInlineSnapshot(` + Array [ + Object { + "actionGroup": "default", + "context": Object {}, + "state": Object {}, + }, + ] + `); + expect(currentActiveAlerts).toMatchInlineSnapshot(`Object {}`); + expect( + Object.values(currentActiveAlerts).map((a) => a.getScheduledActionOptions()) + ).toMatchInlineSnapshot(`Array []`); + expect(recoveredAlerts).toMatchInlineSnapshot(` + Object { + "1": Object { + "meta": Object { + "flapping": true, + "flappingHistory": Array [], + "pendingRecoveredCount": 0, + }, + "state": Object {}, + }, + "2": Object { + "meta": Object { + "flapping": false, + "flappingHistory": Array [], + }, + "state": Object {}, + }, + } + `); + expect(currentRecoveredAlerts).toMatchInlineSnapshot(` + Object { + "1": Object { + "meta": Object { + "flapping": true, + "flappingHistory": Array [], + "pendingRecoveredCount": 0, + }, + "state": Object {}, + }, + "2": Object { + "meta": Object { + "flapping": false, + "flappingHistory": Array [], + }, + "state": Object {}, + }, + } + `); + }); }); 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 e51e35811337b..e25752c4b7b37 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 @@ -8,7 +8,12 @@ import { keys } from 'lodash'; import { RulesSettingsFlappingProperties } from '../../common/rules_settings'; import { Alert } from '../alert'; -import { AlertInstanceState, AlertInstanceContext } from '../types'; +import { + AlertInstanceState, + AlertInstanceContext, + RuleNotifyWhenType, + RuleNotifyWhen, +} from '../types'; export function getAlertsForNotification< State extends AlertInstanceState, @@ -17,15 +22,19 @@ export function getAlertsForNotification< RecoveryActionGroupId extends string >( flappingSettings: RulesSettingsFlappingProperties, + notifyWhen: RuleNotifyWhenType | null, actionGroupId: string, newAlerts: Record> = {}, activeAlerts: Record> = {}, recoveredAlerts: Record> = {}, currentRecoveredAlerts: Record> = {} ) { + const currentActiveAlerts: Record> = {}; + for (const id of keys(activeAlerts)) { const alert = activeAlerts[id]; alert.resetPendingRecoveredCount(); + currentActiveAlerts[id] = alert; } for (const id of keys(currentRecoveredAlerts)) { @@ -53,6 +62,11 @@ export function getAlertsForNotification< ); activeAlerts[id] = newAlert; + // rules with "on status change" should return notifications + if (notifyWhen === RuleNotifyWhen.CHANGE) { + currentActiveAlerts[id] = newAlert; + } + // remove from recovered alerts delete recoveredAlerts[id]; delete currentRecoveredAlerts[id]; @@ -68,6 +82,7 @@ export function getAlertsForNotification< return { newAlerts, activeAlerts, + currentActiveAlerts, recoveredAlerts, currentRecoveredAlerts, }; diff --git a/x-pack/plugins/alerting/server/lib/index.ts b/x-pack/plugins/alerting/server/lib/index.ts index 8f8dccadaaa77..0dd0008964e24 100644 --- a/x-pack/plugins/alerting/server/lib/index.ts +++ b/x-pack/plugins/alerting/server/lib/index.ts @@ -27,7 +27,7 @@ export { } from './rule_execution_status'; export { lastRunFromState, lastRunFromError, lastRunToRaw } from './last_run_status'; export { - updateMonitoring, + resetMonitoringLastRun, getDefaultMonitoring, convertMonitoringFromRawAndVerify, } from './monitoring'; diff --git a/x-pack/plugins/alerting/server/lib/monitoring.test.ts b/x-pack/plugins/alerting/server/lib/monitoring.test.ts index 492e205a99508..5d417c2735681 100644 --- a/x-pack/plugins/alerting/server/lib/monitoring.test.ts +++ b/x-pack/plugins/alerting/server/lib/monitoring.test.ts @@ -5,47 +5,56 @@ * 2.0. */ +import { RuleMonitoring, RuleMonitoringHistory } from '../types'; import { getExecutionDurationPercentiles, updateMonitoring, convertMonitoringFromRawAndVerify, + resetMonitoringLastRun, } from './monitoring'; -import { RuleMonitoring } from '../types'; -const mockHistory = [ +const mockHistory: RuleMonitoringHistory[] = [ { + timestamp: 1655427600000, success: true, duration: 100, }, { + timestamp: 1655427900000, success: true, duration: 200, }, { + timestamp: 1655428200000, success: false, duration: 300, }, { + timestamp: 1655428500000, success: false, duration: 100, }, { + timestamp: 1655428800000, success: false, }, { + timestamp: 1655429100000, success: true, }, { + timestamp: 1655429400000, success: true, duration: 400, }, { + timestamp: 1655429700000, success: true, duration: 500, }, ]; -const mockRuleMonitoring = { +const mockRuleMonitoring: RuleMonitoring = { run: { history: mockHistory, calculated_metrics: { @@ -58,7 +67,7 @@ const mockRuleMonitoring = { }, }, }, -} as RuleMonitoring; +}; describe('getExecutionDurationPercentiles', () => { it('Calculates the percentile given partly undefined durations', () => { @@ -70,23 +79,57 @@ describe('getExecutionDurationPercentiles', () => { it('Returns empty object when given all undefined durations', () => { // remove all duration fields - const nullDurationHistory = mockHistory.map((history) => ({ + const nullDurationHistory: RuleMonitoringHistory[] = mockHistory.map((history) => ({ + timestamp: history.timestamp, success: history.success, })); - const newMockRuleMonitoring = { + const newMockRuleMonitoring: RuleMonitoring = { ...mockRuleMonitoring, run: { ...mockRuleMonitoring.run, history: nullDurationHistory, }, - } as RuleMonitoring; + }; const percentiles = getExecutionDurationPercentiles(newMockRuleMonitoring.run.history); expect(Object.keys(percentiles).length).toEqual(0); }); }); +describe('resetMonitoringLastRun', () => { + it('resets last run metrics to the initial default value', () => { + const result = resetMonitoringLastRun(mockRuleMonitoring); + expect(result.run.last_run.metrics).toEqual({ + duration: 0, + total_search_duration_ms: null, + total_indexing_duration_ms: null, + total_alerts_detected: null, + total_alerts_created: null, + gap_duration_s: null, + }); + }); + + it('preserves last run timestamp', () => { + const expectedTimestamp = mockRuleMonitoring.run.last_run.timestamp; + const result = resetMonitoringLastRun(mockRuleMonitoring); + expect(result.run.last_run.timestamp).toEqual(expectedTimestamp); + }); + + it('preserves other monitoring properties', () => { + const { run: originalRun, ...originalRestOfMonitoringObject } = mockRuleMonitoring; + const { last_run: originalLastRun, ...originalRestOfRunObject } = originalRun; + + const result = resetMonitoringLastRun(mockRuleMonitoring); + + const { run: actualRun, ...actualRestOfMonitoringObject } = result; + const { last_run: actualLastRun, ...actualRestOfRunObject } = actualRun; + + expect(actualRestOfMonitoringObject).toEqual(originalRestOfMonitoringObject); + expect(actualRestOfRunObject).toEqual(originalRestOfRunObject); + }); +}); + describe('updateMonitoring', () => { it('can update monitoring', () => { const result = updateMonitoring({ diff --git a/x-pack/plugins/alerting/server/lib/monitoring.ts b/x-pack/plugins/alerting/server/lib/monitoring.ts index 93da6e2283152..9224600eeffc7 100644 --- a/x-pack/plugins/alerting/server/lib/monitoring.ts +++ b/x-pack/plugins/alerting/server/lib/monitoring.ts @@ -6,9 +6,15 @@ */ import { Logger } from '@kbn/core/server'; import stats from 'stats-lite'; -import { RuleMonitoring, RawRuleMonitoring, RuleMonitoringHistory } from '../types'; +import { + RuleMonitoring, + RawRuleMonitoring, + RuleMonitoringHistory, + RuleMonitoringLastRunMetrics, +} from '../types'; -export const INITIAL_METRICS = { +const INITIAL_LAST_RUN_METRICS: RuleMonitoringLastRunMetrics = { + duration: 0, total_search_duration_ms: null, total_indexing_duration_ms: null, total_alerts_detected: null, @@ -25,29 +31,26 @@ export const getDefaultMonitoring = (timestamp: string): RawRuleMonitoring => { }, last_run: { timestamp, - metrics: INITIAL_METRICS, + metrics: INITIAL_LAST_RUN_METRICS, }, }, }; }; -export const getExecutionDurationPercentiles = (history: RuleMonitoringHistory[]) => { - const durationSamples = history.reduce((duration, historyItem) => { - if (typeof historyItem.duration === 'number') { - return [...duration, historyItem.duration]; - } - return duration; - }, []); +export const resetMonitoringLastRun = (monitoring: RuleMonitoring): RawRuleMonitoring => { + const { run, ...restMonitoring } = monitoring; + const { last_run: lastRun, ...restRun } = run; - if (durationSamples.length) { - return { - p50: stats.percentile(durationSamples as number[], 0.5), - p95: stats.percentile(durationSamples as number[], 0.95), - p99: stats.percentile(durationSamples as number[], 0.99), - }; - } - - return {}; + return { + ...restMonitoring, + run: { + ...restRun, + last_run: { + timestamp: lastRun.timestamp, + metrics: INITIAL_LAST_RUN_METRICS, + }, + }, + }; }; // Immutably updates the monitoring object with timestamp and duration. @@ -60,13 +63,15 @@ export const updateMonitoring = ({ monitoring: RuleMonitoring; timestamp: string; duration?: number; -}) => { - const { run } = monitoring; - const { last_run: lastRun, ...rest } = run; - const { metrics = INITIAL_METRICS } = lastRun; +}): RawRuleMonitoring => { + const { run, ...restMonitoring } = monitoring; + const { last_run: lastRun, ...restRun } = run; + const { metrics = INITIAL_LAST_RUN_METRICS } = lastRun; return { + ...restMonitoring, run: { + ...restRun, last_run: { timestamp, metrics: { @@ -74,7 +79,6 @@ export const updateMonitoring = ({ duration, }, }, - ...rest, }, }; }; @@ -102,3 +106,22 @@ export const convertMonitoringFromRawAndVerify = ( duration: monitoring.run.last_run.metrics.duration, }); }; + +export const getExecutionDurationPercentiles = (history: RuleMonitoringHistory[]) => { + const durationSamples = history.reduce((duration, historyItem) => { + if (typeof historyItem.duration === 'number') { + return [...duration, historyItem.duration]; + } + return duration; + }, []); + + if (durationSamples.length) { + return { + p50: stats.percentile(durationSamples as number[], 0.5), + p95: stats.percentile(durationSamples as number[], 0.95), + p99: stats.percentile(durationSamples as number[], 0.99), + }; + } + + return {}; +}; diff --git a/x-pack/plugins/alerting/server/rules_client/methods/enable.ts b/x-pack/plugins/alerting/server/rules_client/methods/enable.ts index 5b26061120b0a..1aee25fa5adbc 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/enable.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/enable.ts @@ -6,7 +6,7 @@ */ import { RawRule, IntervalSchedule } from '../../types'; -import { updateMonitoring, getNextRun } from '../../lib'; +import { resetMonitoringLastRun, getNextRun } from '../../lib'; import { WriteOperations, AlertingAuthorizationEntity } from '../../authorization'; import { retryIfConflicts } from '../../lib/retry_if_conflicts'; import { ruleAuditEvent, RuleAuditAction } from '../common/audit_events'; @@ -84,11 +84,7 @@ async function enableWithOCC(context: RulesClientContext, { id }: { id: string } ...attributes, ...(!existingApiKey && (await createNewAPIKeySet(context, { attributes, username }))), ...(attributes.monitoring && { - monitoring: updateMonitoring({ - monitoring: attributes.monitoring, - timestamp: now.toISOString(), - duration: 0, - }), + monitoring: resetMonitoringLastRun(attributes.monitoring), }), nextRun: getNextRun({ interval: schedule.interval }), enabled: true, diff --git a/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts index 84226a318f9c3..8d5d681845f8a 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts @@ -439,6 +439,7 @@ describe('create()', () => { "history": Array [], "last_run": Object { "metrics": Object { + "duration": 0, "gap_duration_s": null, "total_alerts_created": null, "total_alerts_detected": null, @@ -658,6 +659,7 @@ describe('create()', () => { "history": Array [], "last_run": Object { "metrics": Object { + "duration": 0, "gap_duration_s": null, "total_alerts_created": null, "total_alerts_detected": null, @@ -2097,6 +2099,7 @@ describe('create()', () => { last_run: { timestamp: '2019-02-12T21:01:22.479Z', metrics: { + duration: 0, gap_duration_s: null, total_alerts_created: null, total_alerts_detected: null, diff --git a/x-pack/plugins/alerting/server/task_runner/fixtures.ts b/x-pack/plugins/alerting/server/task_runner/fixtures.ts index 920afcfbe3035..8558cd6cbd70f 100644 --- a/x-pack/plugins/alerting/server/task_runner/fixtures.ts +++ b/x-pack/plugins/alerting/server/task_runner/fixtures.ts @@ -101,6 +101,7 @@ export const generateSavedObjectParams = ({ last_run: { timestamp: '1970-01-01T00:00:00.000Z', metrics: { + duration: 0, gap_duration_s: null, total_alerts_created: null, total_alerts_detected: null, @@ -281,6 +282,7 @@ export const generateRunnerResult = ({ history: history.map((success) => ({ success, timestamp: 0 })), last_run: { metrics: { + duration: 0, gap_duration_s: null, total_alerts_created: null, total_alerts_detected: null, diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts index f0727024d277f..a94512e1be483 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -427,6 +427,7 @@ export class TaskRunner< ruleRunMetricsStore, shouldLogAndScheduleActionsForAlerts: this.shouldLogAndScheduleActionsForAlerts(), flappingSettings, + notifyWhen, }); }); @@ -459,7 +460,7 @@ export class TaskRunner< this.countUsageOfActionExecutionAfterRuleCancellation(); } else { executionHandlerRunResult = await executionHandler.run({ - ...this.legacyAlertsClient.getProcessedAlerts('active'), + ...this.legacyAlertsClient.getProcessedAlerts('activeCurrent'), ...this.legacyAlertsClient.getProcessedAlerts('recoveredCurrent'), }); } diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts index 84ee0f101d8b8..abe4b2b6c46a7 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts @@ -246,6 +246,7 @@ describe('Task Runner Cancel', () => { history: [], last_run: { metrics: { + duration: 0, gap_duration_s: null, total_alerts_created: null, total_alerts_detected: null, diff --git a/x-pack/plugins/alerting/server/types.ts b/x-pack/plugins/alerting/server/types.ts index e319f315440af..4997d9563d59e 100644 --- a/x-pack/plugins/alerting/server/types.ts +++ b/x-pack/plugins/alerting/server/types.ts @@ -22,6 +22,7 @@ import { } from '@kbn/core/server'; import type { PublicMethodsOf } from '@kbn/utility-types'; import { SharePluginStart } from '@kbn/share-plugin/server'; +import { type FieldMap } from '@kbn/alerts-as-data-utils'; import { RuleTypeRegistry as OrigruleTypeRegistry } from './rule_type_registry'; import { PluginSetupContract, PluginStartContract } from './plugin'; import { RulesClient } from './rules_client'; @@ -51,7 +52,6 @@ import { SanitizedRule, } from '../common'; import { PublicAlertFactory } from './alert/create_alert_factory'; -import { FieldMap } from '../common/alert_schema/field_maps/types'; import { RulesSettingsFlappingProperties } from '../common/rules_settings'; export type WithoutQueryAndParams = Pick>; export type SpaceIdToNamespaceFunction = (spaceId?: string) => string | undefined; @@ -172,6 +172,8 @@ export interface IRuleTypeAlerts { context: string; namespace?: string; fieldMap: FieldMap; + useEcs?: boolean; + useLegacyAlerts?: boolean; } export interface RuleType< diff --git a/x-pack/plugins/alerting/tsconfig.json b/x-pack/plugins/alerting/tsconfig.json index 1f7017de59a2e..6fed19209ad12 100644 --- a/x-pack/plugins/alerting/tsconfig.json +++ b/x-pack/plugins/alerting/tsconfig.json @@ -39,6 +39,8 @@ "@kbn/data-views-plugin", "@kbn/share-plugin", "@kbn/safer-lodash-set", + "@kbn/alerts-as-data-utils", + "@kbn/core-elasticsearch-client-server-mocks", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/apm/dev_docs/testing.md b/x-pack/plugins/apm/dev_docs/testing.md index d6fc05f286d5a..1fa7ac8774b08 100644 --- a/x-pack/plugins/apm/dev_docs/testing.md +++ b/x-pack/plugins/apm/dev_docs/testing.md @@ -72,7 +72,9 @@ node x-pack/plugins/apm/scripts/test/api --runner --basic --updateSnapshots The E2E tests are located in [`x-pack/plugins/apm/ftr_e2e`](../ftr_e2e). -Test runs are recorded to the [Cypress Dashboard](https://dashboard.cypress.io). Tests run on buildkite PR pipeline are parallelized (4 parallel jobs) and are orchestrated by the Cypress dashboard service. It can be configured in [.buildkite/pipelines/pull_request/apm_cypress.yml](https://github.com/elastic/kibana/blob/main/.buildkite/pipelines/pull_request/apm_cypress.yml) with the property `parallelism`. +When PR is labeled with `apm:cypress-record`, test runs are recorded to the [Cypress Dashboard](https://dashboard.cypress.io). + +Tests run on buildkite PR pipeline are parallelized (4 parallel jobs) and are orchestrated by the Cypress dashboard service. It can be configured in [.buildkite/pipelines/pull_request/apm_cypress.yml](https://github.com/elastic/kibana/blob/main/.buildkite/pipelines/pull_request/apm_cypress.yml) with the property `parallelism`. ```yml ... diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/errors/error_details.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/errors/error_details.cy.ts index 2c837abd2bc4d..8714fe8f07873 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/errors/error_details.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/errors/error_details.cy.ts @@ -48,7 +48,7 @@ describe('Error details', () => { }); describe('when error has no occurrences', () => { - it('shows an empty message', () => { + it('shows zero occurrences', () => { cy.visitKibana( url.format({ pathname: @@ -60,7 +60,7 @@ describe('Error details', () => { }, }) ); - cy.contains('No data to display'); + cy.contains('0 occ'); }); }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/instances_table.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/instances_table.cy.ts index 578b116a10592..bda4e78f56c48 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/instances_table.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/instances_table.cy.ts @@ -24,7 +24,7 @@ const serviceRumOverviewHref = url.format({ const testServiveHref = url.format({ pathname: '/app/apm/services/test-service/overview', - query: { rangeFrom: start, rangeTo: end }, + query: { rangeFrom: start, rangeTo: end, transactionType: 'type' }, }); const serviceNodeName = 'opbeans-java-prod-1'; diff --git a/x-pack/plugins/apm/public/components/app/alerts_overview/index.tsx b/x-pack/plugins/apm/public/components/app/alerts_overview/index.tsx index 541a4619300f3..d5cd8c530332e 100644 --- a/x-pack/plugins/apm/public/components/app/alerts_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/alerts_overview/index.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useState, useMemo } from 'react'; +import React, { useState, useMemo, useEffect } from 'react'; import { useHistory } from 'react-router-dom'; import { ObservabilityAlertSearchBar } from '@kbn/observability-plugin/public'; import { AlertStatus } from '@kbn/observability-plugin/common/typings'; @@ -25,7 +25,7 @@ export function AlertsOverview() { const history = useHistory(); const { path: { serviceName }, - query: { environment, rangeFrom, rangeTo, kuery }, + query: { environment, rangeFrom, rangeTo, kuery, alertStatus }, } = useAnyOfApmParams( '/services/{serviceName}/alerts', '/mobile-services/{serviceName}/alerts' @@ -35,6 +35,12 @@ export function AlertsOverview() { useState(ALERT_STATUS_ALL); const [esQuery, setEsQuery] = useState<{ bool: BoolQuery }>(); + useEffect(() => { + if (alertStatus) { + setAlertStatusFilter(alertStatus as AlertStatus); + } + }, [alertStatus]); + const { triggersActionsUi: { getAlertsStateTable: AlertsStateTable, diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/service_list/index.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/service_list/index.tsx index 9241c8bcae7a5..e921f3883e392 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/service_list/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_inventory/service_list/index.tsx @@ -15,6 +15,7 @@ import { RIGHT_ALIGNMENT, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { ALERT_STATUS_ACTIVE } from '@kbn/rule-data-utils'; import { TypeOf } from '@kbn/typed-react-router-config'; import React, { useMemo } from 'react'; import { ServiceHealthStatus } from '../../../../../common/service_health_status'; @@ -41,9 +42,9 @@ import { getTimeSeriesColor, } from '../../../shared/charts/helper/get_timeseries_color'; import { EnvironmentBadge } from '../../../shared/environment_badge'; +import { ServiceLink } from '../../../shared/links/apm/service_link'; import { ListMetric } from '../../../shared/list_metric'; import { ITableColumn, ManagedTable } from '../../../shared/managed_table'; -import { ServiceLink } from '../../../shared/links/apm/service_link'; import { HealthBadge } from './health_badge'; type ServicesDetailedStatisticsAPIResponse = @@ -104,7 +105,10 @@ export function getServiceColumns({ color="danger" href={link('/services/{serviceName}/alerts', { path: { serviceName }, - query, + query: { + ...query, + alertStatus: ALERT_STATUS_ACTIVE, + }, })} > {alertsCount} diff --git a/x-pack/plugins/apm/public/components/app/service_map/disabled_prompt.tsx b/x-pack/plugins/apm/public/components/app/service_map/disabled_prompt.tsx new file mode 100644 index 0000000000000..77f8a0ea505c5 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/service_map/disabled_prompt.tsx @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiEmptyPrompt } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; + +export function DisabledPrompt() { + return ( + + {i18n.translate('xpack.apm.serviceMap.disabledTitle', { + defaultMessage: 'Service map is disabled', + })} +

    + } + body={ +

    + {i18n.translate('xpack.apm.serviceMap.disabledDescription', { + defaultMessage: + 'The service map has been disabled. It can be enabled via `xpack.apm.serviceMapEnabled`', + })} +

    + } + /> + ); +} diff --git a/x-pack/plugins/apm/public/components/app/service_map/index.tsx b/x-pack/plugins/apm/public/components/app/service_map/index.tsx index bc75102fa7d67..2b3d8c8eaf15c 100644 --- a/x-pack/plugins/apm/public/components/app/service_map/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_map/index.tsx @@ -12,6 +12,7 @@ import { EuiPanel, } from '@elastic/eui'; import React, { ReactNode } from 'react'; +import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; import { isActivePlatinumLicense } from '../../../../common/license_check'; import { invalidLicenseMessage, @@ -34,6 +35,7 @@ import { useServiceName } from '../../../hooks/use_service_name'; import { useApmParams, useAnyOfApmParams } from '../../../hooks/use_apm_params'; import { Environment } from '../../../../common/environment_rt'; import { useTimeRange } from '../../../hooks/use_time_range'; +import { DisabledPrompt } from './disabled_prompt'; function PromptContainer({ children }: { children: ReactNode }) { return ( @@ -114,8 +116,8 @@ export function ServiceMap({ }) { const theme = useTheme(); const license = useLicenseContext(); - const serviceName = useServiceName(); + const { config } = useApmPluginContext(); const { data = { elements: [] }, @@ -124,7 +126,11 @@ export function ServiceMap({ } = useFetcher( (callApmApi) => { // When we don't have a license or a valid license, don't make the request. - if (!license || !isActivePlatinumLicense(license)) { + if ( + !license || + !isActivePlatinumLicense(license) || + !config.serviceMapEnabled + ) { return; } @@ -142,7 +148,16 @@ export function ServiceMap({ }, }); }, - [license, serviceName, environment, start, end, serviceGroupId, kuery] + [ + license, + serviceName, + environment, + start, + end, + serviceGroupId, + kuery, + config.serviceMapEnabled, + ] ); const { ref, height } = useRefDimensions(); @@ -163,6 +178,14 @@ export function ServiceMap({ ); } + if (!config.serviceMapEnabled) { + return ( + + + + ); + } + if (status === FETCH_STATUS.SUCCESS && data.elements.length === 0) { return ( diff --git a/x-pack/plugins/apm/public/components/app/service_overview/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/index.tsx index 9c7a52dc5859d..3e2d178357a80 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/index.tsx @@ -5,37 +5,37 @@ * 2.0. */ -import React from 'react'; import { i18n } from '@kbn/i18n'; +import React from 'react'; import { - EuiFlexGroupProps, EuiFlexGroup, + EuiFlexGroupProps, EuiFlexItem, EuiLink, EuiPanel, } from '@elastic/eui'; +import { + isRumAgentName, + isServerlessAgent, +} from '../../../../common/agent_name'; import { AnnotationsContextProvider } from '../../../context/annotations/annotations_context'; import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; import { ChartPointerEventContextProvider } from '../../../context/chart_pointer_event/chart_pointer_event_context'; -import { useBreakpoints } from '../../../hooks/use_breakpoints'; import { useApmParams } from '../../../hooks/use_apm_params'; -import { useTimeRange } from '../../../hooks/use_time_range'; import { useApmRouter } from '../../../hooks/use_apm_router'; +import { useBreakpoints } from '../../../hooks/use_breakpoints'; +import { useTimeRange } from '../../../hooks/use_time_range'; +import { AggregatedTransactionsBadge } from '../../shared/aggregated_transactions_badge'; +import { FailedTransactionRateChart } from '../../shared/charts/failed_transaction_rate_chart'; import { LatencyChart } from '../../shared/charts/latency_chart'; import { TransactionBreakdownChart } from '../../shared/charts/transaction_breakdown_chart'; import { TransactionColdstartRateChart } from '../../shared/charts/transaction_coldstart_rate_chart'; -import { FailedTransactionRateChart } from '../../shared/charts/failed_transaction_rate_chart'; +import { TransactionsTable } from '../../shared/transactions_table'; import { ServiceOverviewDependenciesTable } from './service_overview_dependencies_table'; import { ServiceOverviewErrorsTable } from './service_overview_errors_table'; import { ServiceOverviewInstancesChartAndTable } from './service_overview_instances_chart_and_table'; import { ServiceOverviewThroughputChart } from './service_overview_throughput_chart'; -import { TransactionsTable } from '../../shared/transactions_table'; -import { AggregatedTransactionsBadge } from '../../shared/aggregated_transactions_badge'; -import { - isRumAgentName, - isServerlessAgent, -} from '../../../../common/agent_name'; /** * The height a chart should be if it's next to a table with 5 rows and a title. * Add the height of the pagination row. diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview.stories.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview.stories.tsx index e269f198b5954..f8b5dc3e0b992 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview.stories.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview.stories.tsx @@ -8,6 +8,7 @@ import type { CoreStart } from '@kbn/core/public'; import { Meta, Story } from '@storybook/react'; import React from 'react'; +import { FETCH_STATUS } from '../../../hooks/use_fetcher'; import { ServiceOverview } from '.'; import type { ApmPluginContextValue } from '../../../context/apm_plugin/apm_plugin_context'; import { MockApmPluginStorybook } from '../../../context/apm_plugin/mock_apm_plugin_storybook'; @@ -19,6 +20,8 @@ const stories: Meta<{}> = { decorators: [ (StoryComponent) => { const serviceName = 'testServiceName'; + const transactionType = 'type'; + const transactionTypeStatus = FETCH_STATUS.SUCCESS; const mockCore = { http: { get: (endpoint: string) => { @@ -37,6 +40,8 @@ const stories: Meta<{}> = { } as unknown as CoreStart; const serviceContextValue = { serviceName, + transactionType, + transactionTypeStatus, } as unknown as APMServiceContextValue; return ( diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_chart_and_table.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_chart_and_table.tsx index 13bd5324f6444..ab6d66ab2130d 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_chart_and_table.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_chart_and_table.tsx @@ -67,7 +67,7 @@ export function ServiceOverviewInstancesChartAndTable({ chartHeight, serviceName, }: ServiceOverviewInstancesChartAndTableProps) { - const { transactionType } = useApmServiceContext(); + const { transactionType, transactionTypeStatus } = useApmServiceContext(); const [tableOptions, setTableOptions] = useState({ pageIndex: 0, sort: DEFAULT_SORT, @@ -95,6 +95,10 @@ export function ServiceOverviewInstancesChartAndTable({ status: mainStatsStatus, } = useFetcher( (callApmApi) => { + if (!transactionType && transactionTypeStatus === FETCH_STATUS.SUCCESS) { + return Promise.resolve(INITIAL_STATE_MAIN_STATS); + } + if (!start || !end || !transactionType || !latencyAggregationType) { return; } @@ -125,9 +129,9 @@ export function ServiceOverviewInstancesChartAndTable({ return { // Everytime the main statistics is refetched, updates the requestId making the detailed API to be refetched. requestId: uuidv4(), - currentPeriodItems: response.currentPeriod, - currentPeriodItemsCount: response.currentPeriod.length, - previousPeriodItems: response.previousPeriod, + currentPeriodItems: response?.currentPeriod ?? [], + currentPeriodItemsCount: response?.currentPeriod.length, + previousPeriodItems: response?.previousPeriod, }; }); }, @@ -140,6 +144,7 @@ export function ServiceOverviewInstancesChartAndTable({ end, serviceName, transactionType, + transactionTypeStatus, pageIndex, field, direction, diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_throughput_chart.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_throughput_chart.tsx index fcc4f3a97cd5c..30df64a9b4d0c 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_throughput_chart.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_throughput_chart.tsx @@ -21,7 +21,7 @@ import { asExactTransactionRate } from '../../../../common/utils/formatters'; import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; import { useEnvironmentsContext } from '../../../context/environments_context/use_environments_context'; import { useAnyOfApmParams } from '../../../hooks/use_apm_params'; -import { useFetcher } from '../../../hooks/use_fetcher'; +import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher'; import { usePreferredServiceAnomalyTimeseries } from '../../../hooks/use_preferred_service_anomaly_timeseries'; import { useTimeRange } from '../../../hooks/use_time_range'; import { TimeseriesChartWithContext } from '../../shared/charts/timeseries_chart_with_context'; @@ -60,12 +60,17 @@ export function ServiceOverviewThroughputChart({ const { start, end } = useTimeRange({ rangeFrom, rangeTo }); - const { transactionType, serviceName } = useApmServiceContext(); + const { transactionType, serviceName, transactionTypeStatus } = + useApmServiceContext(); const comparisonChartTheme = getComparisonChartTheme(); const { data = INITIAL_STATE, status } = useFetcher( (callApmApi) => { + if (!transactionType && transactionTypeStatus === FETCH_STATUS.SUCCESS) { + return Promise.resolve(INITIAL_STATE); + } + if (serviceName && transactionType && start && end) { return callApmApi( 'GET /internal/apm/services/{serviceName}/throughput', @@ -98,6 +103,7 @@ export function ServiceOverviewThroughputChart({ start, end, transactionType, + transactionTypeStatus, offset, transactionName, comparisonEnabled, @@ -111,7 +117,7 @@ export function ServiceOverviewThroughputChart({ const previousPeriodLabel = usePreviousPeriodLabel(); const timeseries = [ { - data: data.currentPeriod, + data: data?.currentPeriod ?? [], type: 'linemark', color: currentPeriodColor, title: i18n.translate('xpack.apm.serviceOverview.throughtputChartTitle', { @@ -121,7 +127,7 @@ export function ServiceOverviewThroughputChart({ ...(comparisonEnabled ? [ { - data: data.previousPeriod, + data: data?.previousPeriod ?? [], type: 'area', color: previousPeriodColor, title: previousPeriodLabel, diff --git a/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx index 21a51babd04b3..21b830fb314af 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx @@ -8,6 +8,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiSpacer } from '@elastic/eui'; import React from 'react'; import { useHistory } from 'react-router-dom'; +import { isServerlessAgent } from '../../../../common/agent_name'; import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; import { useApmParams } from '../../../hooks/use_apm_params'; import { useTimeRange } from '../../../hooks/use_time_range'; @@ -15,7 +16,6 @@ import { AggregatedTransactionsBadge } from '../../shared/aggregated_transaction import { TransactionCharts } from '../../shared/charts/transaction_charts'; import { replace } from '../../shared/links/url_helpers'; import { TransactionsTable } from '../../shared/transactions_table'; -import { isServerlessAgent } from '../../../../common/agent_name'; export function TransactionOverview() { const { diff --git a/x-pack/plugins/apm/public/components/app/transaction_overview/transaction_overview.test.tsx b/x-pack/plugins/apm/public/components/app/transaction_overview/transaction_overview.test.tsx index 5b3118e527fa4..f27aab3724ab5 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_overview/transaction_overview.test.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_overview/transaction_overview.test.tsx @@ -49,7 +49,10 @@ function setup({ // mock transaction types jest .spyOn(useServiceTransactionTypesHook, 'useServiceTransactionTypesFetcher') - .mockReturnValue(serviceTransactionTypes); + .mockReturnValue({ + transactionTypes: serviceTransactionTypes, + status: useFetcherHook.FETCH_STATUS.SUCCESS, + }); // mock agent jest diff --git a/x-pack/plugins/apm/public/components/routing/mobile_service_detail/index.tsx b/x-pack/plugins/apm/public/components/routing/mobile_service_detail/index.tsx index 22a2f27660740..7f07055b99abb 100644 --- a/x-pack/plugins/apm/public/components/routing/mobile_service_detail/index.tsx +++ b/x-pack/plugins/apm/public/components/routing/mobile_service_detail/index.tsx @@ -187,16 +187,23 @@ export const mobileServiceDetail = { hidden: true, }, }), - '/mobile-services/{serviceName}/alerts': page({ - tab: 'alerts', - title: i18n.translate('xpack.apm.views.alerts.title', { - defaultMessage: 'Alerts', + '/mobile-services/{serviceName}/alerts': { + ...page({ + tab: 'alerts', + title: i18n.translate('xpack.apm.views.alerts.title', { + defaultMessage: 'Alerts', + }), + element: , + searchBarOptions: { + hidden: true, + }, }), - element: , - searchBarOptions: { - hidden: true, - }, - }), + params: t.partial({ + query: t.partial({ + alertStatus: t.string, + }), + }), + }, '/mobile-services/{serviceName}/': { element: , }, diff --git a/x-pack/plugins/apm/public/components/routing/service_detail/index.tsx b/x-pack/plugins/apm/public/components/routing/service_detail/index.tsx index 9625c35b1a011..becd09f798979 100644 --- a/x-pack/plugins/apm/public/components/routing/service_detail/index.tsx +++ b/x-pack/plugins/apm/public/components/routing/service_detail/index.tsx @@ -6,6 +6,10 @@ */ import { i18n } from '@kbn/i18n'; import { toBooleanRt, toNumberRt } from '@kbn/io-ts-utils'; +import { + ALERT_STATUS_ACTIVE, + ALERT_STATUS_RECOVERED, +} from '@kbn/rule-data-utils'; import { Outlet } from '@kbn/typed-react-router-config'; import * as t from 'io-ts'; import qs from 'query-string'; @@ -17,7 +21,7 @@ import { environmentRt } from '../../../../common/environment_rt'; import { LatencyAggregationType } from '../../../../common/latency_aggregation_types'; import { TimeRangeMetadataContextProvider } from '../../../context/time_range_metadata/time_range_metadata_context'; import { useApmParams } from '../../../hooks/use_apm_params'; -import { AlertsOverview } from '../../app/alerts_overview'; +import { AlertsOverview, ALERT_STATUS_ALL } from '../../app/alerts_overview'; import { ErrorGroupDetails } from '../../app/error_group_details'; import { ErrorGroupOverview } from '../../app/error_group_overview'; import { InfraOverview } from '../../app/infra_overview'; @@ -340,16 +344,27 @@ export const serviceDetail = { }), }), }, - '/services/{serviceName}/alerts': page({ - tab: 'alerts', - title: i18n.translate('xpack.apm.views.alerts.title', { - defaultMessage: 'Alerts', + '/services/{serviceName}/alerts': { + ...page({ + tab: 'alerts', + title: i18n.translate('xpack.apm.views.alerts.title', { + defaultMessage: 'Alerts', + }), + element: , + searchBarOptions: { + hidden: true, + }, }), - element: , - searchBarOptions: { - hidden: true, - }, - }), + params: t.partial({ + query: t.partial({ + alertStatus: t.union([ + t.literal(ALERT_STATUS_ACTIVE), + t.literal(ALERT_STATUS_RECOVERED), + t.literal(ALERT_STATUS_ALL), + ]), + }), + }), + }, '/services/{serviceName}/': { element: , }, diff --git a/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/analyze_data_button.stories.tsx b/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/analyze_data_button.stories.tsx index 57a0e789267b1..a08e575dcd752 100644 --- a/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/analyze_data_button.stories.tsx +++ b/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/analyze_data_button.stories.tsx @@ -49,6 +49,7 @@ export default { { + if (!transactionType && transactionTypeStatus === FETCH_STATUS.SUCCESS) { + return Promise.resolve(INITIAL_STATE); + } + if (transactionType && serviceName && start && end) { return callApmApi( 'GET /internal/apm/services/{serviceName}/transactions/charts/error_rate', @@ -115,6 +120,7 @@ export function FailedTransactionRateChart({ start, end, transactionType, + transactionTypeStatus, transactionName, offset, comparisonEnabled, @@ -128,7 +134,7 @@ export function FailedTransactionRateChart({ const previousPeriodLabel = usePreviousPeriodLabel(); const timeseries = [ { - data: data.currentPeriod.timeseries, + data: data?.currentPeriod?.timeseries ?? [], type: 'linemark', color: currentPeriodColor, title: i18n.translate('xpack.apm.errorRate.chart.errorRate', { @@ -138,7 +144,7 @@ export function FailedTransactionRateChart({ ...(comparisonEnabled ? [ { - data: data.previousPeriod.timeseries, + data: data?.previousPeriod?.timeseries ?? [], type: 'area', color: previousPeriodColor, title: previousPeriodLabel, diff --git a/x-pack/plugins/apm/public/components/shared/charts/helper/helper.ts b/x-pack/plugins/apm/public/components/shared/charts/helper/helper.ts index d203bd8cfe022..6558c6fd03af6 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/helper/helper.ts +++ b/x-pack/plugins/apm/public/components/shared/charts/helper/helper.ts @@ -39,6 +39,7 @@ export const onBrushEnd = ({ export function isTimeseriesEmpty(timeseries?: Array>) { return ( !timeseries || + timeseries.length === 0 || timeseries .map((serie) => serie.data) .flat() diff --git a/x-pack/plugins/apm/public/components/shared/charts/latency_chart/latency_chart.stories.tsx b/x-pack/plugins/apm/public/components/shared/charts/latency_chart/latency_chart.stories.tsx index 23ec2140f5be7..8f084b17381ec 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/latency_chart/latency_chart.stories.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/latency_chart/latency_chart.stories.tsx @@ -84,6 +84,7 @@ const stories: Meta = { value={{ serviceName, transactionType, + transactionTypeStatus: FETCH_STATUS.SUCCESS, transactionTypes: [], fallbackToTransactions: false, serviceAgentStatus: FETCH_STATUS.SUCCESS, diff --git a/x-pack/plugins/apm/public/components/shared/charts/transaction_breakdown_chart/use_transaction_breakdown.ts b/x-pack/plugins/apm/public/components/shared/charts/transaction_breakdown_chart/use_transaction_breakdown.ts index 742bef523a143..538e7bb26fef3 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/transaction_breakdown_chart/use_transaction_breakdown.ts +++ b/x-pack/plugins/apm/public/components/shared/charts/transaction_breakdown_chart/use_transaction_breakdown.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { useFetcher } from '../../../../hooks/use_fetcher'; +import { FETCH_STATUS, useFetcher } from '../../../../hooks/use_fetcher'; import { useLegacyUrlParams } from '../../../../context/url_params_context/use_url_params'; import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context'; import { useAnyOfApmParams } from '../../../../hooks/use_apm_params'; @@ -31,7 +31,8 @@ export function useTransactionBreakdown({ const { start, end } = useTimeRange({ rangeFrom, rangeTo }); - const { transactionType, serviceName } = useApmServiceContext(); + const { transactionType, serviceName, transactionTypeStatus } = + useApmServiceContext(); const { data = { timeseries: undefined }, @@ -39,6 +40,10 @@ export function useTransactionBreakdown({ status, } = useFetcher( (callApmApi) => { + if (!transactionType && transactionTypeStatus === FETCH_STATUS.SUCCESS) { + return Promise.resolve({ timeseries: undefined }); + } + if (serviceName && start && end && transactionType) { return callApmApi( 'GET /internal/apm/services/{serviceName}/transaction/charts/breakdown', @@ -65,6 +70,7 @@ export function useTransactionBreakdown({ start, end, transactionType, + transactionTypeStatus, transactionName, ] ); diff --git a/x-pack/plugins/apm/public/components/shared/charts/transaction_coldstart_rate_chart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/transaction_coldstart_rate_chart/index.tsx index 3dfc22a1c2809..b236f92d0f892 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/transaction_coldstart_rate_chart/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/transaction_coldstart_rate_chart/index.tsx @@ -18,7 +18,7 @@ import { usePreviousPeriodLabel } from '../../../../hooks/use_previous_period_te import { isTimeComparison } from '../../time_comparison/get_comparison_options'; import { APIReturnType } from '../../../../services/rest/create_call_apm_api'; import { asPercent } from '../../../../../common/utils/formatters'; -import { useFetcher } from '../../../../hooks/use_fetcher'; +import { FETCH_STATUS, useFetcher } from '../../../../hooks/use_fetcher'; import { useTheme } from '../../../../hooks/use_theme'; import { TimeseriesChartWithContext } from '../timeseries_chart_with_context'; import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context'; @@ -71,7 +71,8 @@ export function TransactionColdstartRateChart({ const { start, end } = useTimeRange({ rangeFrom, rangeTo }); - const { serviceName, transactionType } = useApmServiceContext(); + const { serviceName, transactionType, transactionTypeStatus } = + useApmServiceContext(); const comparisonChartTheme = getComparisonChartTheme(); const endpoint = transactionName @@ -80,6 +81,10 @@ export function TransactionColdstartRateChart({ const { data = INITIAL_STATE, status } = useFetcher( (callApmApi) => { + if (!transactionType && transactionTypeStatus === FETCH_STATUS.SUCCESS) { + return Promise.resolve(INITIAL_STATE); + } + if (transactionType && serviceName && start && end) { return callApmApi(endpoint, { params: { @@ -109,6 +114,7 @@ export function TransactionColdstartRateChart({ start, end, transactionType, + transactionTypeStatus, transactionName, offset, endpoint, @@ -119,7 +125,7 @@ export function TransactionColdstartRateChart({ const timeseries = [ { - data: data.currentPeriod.transactionColdstartRate, + data: data?.currentPeriod?.transactionColdstartRate ?? [], type: 'linemark', color: theme.eui.euiColorVis5, title: i18n.translate('xpack.apm.coldstartRate.chart.coldstartRate', { @@ -129,7 +135,7 @@ export function TransactionColdstartRateChart({ ...(comparisonEnabled ? [ { - data: data.previousPeriod.transactionColdstartRate, + data: data?.previousPeriod?.transactionColdstartRate ?? [], type: 'area', color: theme.eui.euiColorMediumShade, title: previousPeriodLabel, diff --git a/x-pack/plugins/apm/public/components/shared/search_bar/search_bar.test.tsx b/x-pack/plugins/apm/public/components/shared/search_bar/search_bar.test.tsx index f07fc415eaf7d..27eecaf573bf6 100644 --- a/x-pack/plugins/apm/public/components/shared/search_bar/search_bar.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/search_bar/search_bar.test.tsx @@ -44,7 +44,10 @@ function setup({ // mock transaction types jest .spyOn(useServiceTransactionTypesHook, 'useServiceTransactionTypesFetcher') - .mockReturnValue(serviceTransactionTypes); + .mockReturnValue({ + transactionTypes: serviceTransactionTypes, + status: useFetcherHook.FETCH_STATUS.SUCCESS, + }); // mock transaction types jest diff --git a/x-pack/plugins/apm/public/components/shared/transactions_table/index.tsx b/x-pack/plugins/apm/public/components/shared/transactions_table/index.tsx index 3c2520e9b219e..fc97216c4ae2f 100644 --- a/x-pack/plugins/apm/public/components/shared/transactions_table/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/transactions_table/index.tsx @@ -132,7 +132,7 @@ export function TransactionsTable({ const { data = INITIAL_STATE, status } = useFetcher( (callApmApi) => { if (!start || !end || !latencyAggregationType || !transactionType) { - return; + return Promise.resolve(undefined); } return callApmApi( 'GET /internal/apm/services/{serviceName}/transactions/groups/main_statistics', @@ -260,10 +260,6 @@ export function TransactionsTable({ transactionOverflowCount, }); - const isLoading = status === FETCH_STATUS.LOADING; - const isNotInitiated = status === FETCH_STATUS.NOT_INITIATED; - const hasFailed = status === FETCH_STATUS.FAILURE; - const pagination = useMemo( () => ({ pageIndex: index, @@ -338,13 +334,14 @@ export function TransactionsTable({ ({ serviceName: '', + transactionTypeStatus: FETCH_STATUS.NOT_INITIATED, transactionTypes: [], fallbackToTransactions: false, serviceAgentStatus: FETCH_STATUS.NOT_INITIATED, @@ -65,11 +67,12 @@ export function ApmServiceContextProvider({ end, }); - const transactionTypes = useServiceTransactionTypesFetcher({ - serviceName, - start, - end, - }); + const { transactionTypes, status: transactionTypeStatus } = + useServiceTransactionTypesFetcher({ + serviceName, + start, + end, + }); const currentTransactionType = getOrRedirectToTransactionType({ transactionType: query.transactionType, @@ -89,6 +92,7 @@ export function ApmServiceContextProvider({ agentName, serverlessType, transactionType: currentTransactionType, + transactionTypeStatus, transactionTypes, runtimeName, fallbackToTransactions, diff --git a/x-pack/plugins/apm/public/context/apm_service/use_service_transaction_types_fetcher.tsx b/x-pack/plugins/apm/public/context/apm_service/use_service_transaction_types_fetcher.tsx index 96a4141e824cc..fa00f7f45e743 100644 --- a/x-pack/plugins/apm/public/context/apm_service/use_service_transaction_types_fetcher.tsx +++ b/x-pack/plugins/apm/public/context/apm_service/use_service_transaction_types_fetcher.tsx @@ -18,7 +18,7 @@ export function useServiceTransactionTypesFetcher({ start?: string; end?: string; }) { - const { data = INITIAL_DATA } = useFetcher( + const { data = INITIAL_DATA, status } = useFetcher( (callApmApi) => { if (serviceName && start && end) { return callApmApi( @@ -35,5 +35,5 @@ export function useServiceTransactionTypesFetcher({ [serviceName, start, end] ); - return data.transactionTypes; + return { transactionTypes: data.transactionTypes, status }; } diff --git a/x-pack/plugins/apm/public/hooks/use_transaction_latency_chart_fetcher.ts b/x-pack/plugins/apm/public/hooks/use_transaction_latency_chart_fetcher.ts index 32b0c06e99198..d3640afe9f9ea 100644 --- a/x-pack/plugins/apm/public/hooks/use_transaction_latency_chart_fetcher.ts +++ b/x-pack/plugins/apm/public/hooks/use_transaction_latency_chart_fetcher.ts @@ -6,14 +6,14 @@ */ import { useMemo } from 'react'; -import { usePreviousPeriodLabel } from './use_previous_period_text'; import { isTimeComparison } from '../components/shared/time_comparison/get_comparison_options'; -import { useFetcher } from './use_fetcher'; -import { useLegacyUrlParams } from '../context/url_params_context/use_url_params'; import { useApmServiceContext } from '../context/apm_service/use_apm_service_context'; +import { useLegacyUrlParams } from '../context/url_params_context/use_url_params'; import { getLatencyChartSelector } from '../selectors/latency_chart_selectors'; -import { useTimeRange } from './use_time_range'; import { useAnyOfApmParams } from './use_apm_params'; +import { FETCH_STATUS, useFetcher } from './use_fetcher'; +import { usePreviousPeriodLabel } from './use_previous_period_text'; +import { useTimeRange } from './use_time_range'; export function useTransactionLatencyChartsFetcher({ kuery, @@ -22,7 +22,8 @@ export function useTransactionLatencyChartsFetcher({ kuery: string; environment: string; }) { - const { transactionType, serviceName } = useApmServiceContext(); + const { transactionType, serviceName, transactionTypeStatus } = + useApmServiceContext(); const { urlParams: { transactionName, latencyAggregationType }, } = useLegacyUrlParams(); @@ -38,6 +39,10 @@ export function useTransactionLatencyChartsFetcher({ const { data, error, status } = useFetcher( (callApmApi) => { + if (!transactionType && transactionTypeStatus === FETCH_STATUS.SUCCESS) { + return Promise.resolve(undefined); + } + if ( serviceName && start && @@ -76,6 +81,7 @@ export function useTransactionLatencyChartsFetcher({ end, transactionName, transactionType, + transactionTypeStatus, latencyAggregationType, offset, comparisonEnabled, diff --git a/x-pack/plugins/apm/server/plugin.ts b/x-pack/plugins/apm/server/plugin.ts index 86907fdd570be..5b98c8e437782 100644 --- a/x-pack/plugins/apm/server/plugin.ts +++ b/x-pack/plugins/apm/server/plugin.ts @@ -15,10 +15,10 @@ import { PluginInitializerContext, } from '@kbn/core/server'; import { isEmpty, mapValues } from 'lodash'; -import { mappingFromFieldMap } from '@kbn/rule-registry-plugin/common/mapping_from_field_map'; import { experimentalRuleFieldMap } from '@kbn/rule-registry-plugin/common/assets/field_maps/experimental_rule_field_map'; import { Dataset } from '@kbn/rule-registry-plugin/server'; import { UI_SETTINGS } from '@kbn/data-plugin/common'; +import { mappingFromFieldMap } from '@kbn/alerting-plugin/common'; import { APMConfig, APM_SERVER_FEATURE_ID } from '.'; import { APM_FEATURE, registerFeaturesUsage } from './feature'; import { registerApmRuleTypes } from './routes/alerts/register_apm_rule_types'; @@ -130,25 +130,32 @@ export class APMPlugin ...experimentalRuleFieldMap, [SERVICE_NAME]: { type: 'keyword', + required: false, }, [SERVICE_ENVIRONMENT]: { type: 'keyword', + required: false, }, [TRANSACTION_TYPE]: { type: 'keyword', + required: false, }, [PROCESSOR_EVENT]: { type: 'keyword', + required: false, }, [AGENT_NAME]: { type: 'keyword', + required: false, }, [SERVICE_LANGUAGE_NAME]: { type: 'keyword', + required: false, }, labels: { type: 'object', dynamic: true, + required: false, }, }, 'strict' diff --git a/x-pack/plugins/apm/server/routes/apm_routes/register_apm_server_routes.ts b/x-pack/plugins/apm/server/routes/apm_routes/register_apm_server_routes.ts index 6de9c99cdbcd3..65747a10ae29d 100644 --- a/x-pack/plugins/apm/server/routes/apm_routes/register_apm_server_routes.ts +++ b/x-pack/plugins/apm/server/routes/apm_routes/register_apm_server_routes.ts @@ -19,6 +19,7 @@ import { } from '@kbn/server-route-repository'; import { jsonRt, mergeRt } from '@kbn/io-ts-utils'; import { InspectResponse } from '@kbn/observability-plugin/typings/common'; +import apm from 'elastic-apm-node'; import { pickKeys } from '../../../common/utils/pick_keys'; import { APMRouteHandlerResources, TelemetryUsageCounter } from '../typings'; import type { ApmPluginRequestHandlerContext } from '../typings'; @@ -181,6 +182,9 @@ export function registerRoutes({ opts.statusCode = error.output.statusCode; } + // capture error with APM node agent + apm.captureError(error); + return response.custom(opts); } finally { // cleanup diff --git a/x-pack/plugins/apm/server/routes/source_maps/schedule_source_map_migration.ts b/x-pack/plugins/apm/server/routes/source_maps/schedule_source_map_migration.ts index 08c8a29ef6712..3ab48db456845 100644 --- a/x-pack/plugins/apm/server/routes/source_maps/schedule_source_map_migration.ts +++ b/x-pack/plugins/apm/server/routes/source_maps/schedule_source_map_migration.ts @@ -44,7 +44,6 @@ export async function scheduleSourceMapMigration({ description: `Migrates fleet source map artifacts to "${APM_SOURCE_MAP_INDEX}" index`, timeout: '1h', maxAttempts: 5, - maxConcurrency: 1, createTaskRunner() { const taskState: TaskState = { isAborted: false }; diff --git a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.component.tsx b/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.component.tsx index ed84d13641260..18d911a7c2342 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.component.tsx +++ b/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.component.tsx @@ -14,7 +14,7 @@ import { import { i18n } from '@kbn/i18n'; import { EmbeddableFactoryDefinition } from '@kbn/embeddable-plugin/public'; import { BaseVisType, VisTypeAlias } from '@kbn/visualizations-plugin/public'; -import { SolutionToolbarPopover } from '@kbn/presentation-util-plugin/public'; +import { ToolbarPopover } from '@kbn/shared-ux-button-toolbar'; const strings = { getEditorMenuButtonLabel: () => @@ -145,11 +145,9 @@ export const EditorMenu: FC = ({ ]; return ( - @@ -165,6 +163,6 @@ export const EditorMenu: FC = ({ data-test-subj="canvasEditorContextMenu" /> )} - + ); }; diff --git a/x-pack/plugins/canvas/public/components/workpad_header/element_menu/element_menu.component.tsx b/x-pack/plugins/canvas/public/components/workpad_header/element_menu/element_menu.component.tsx index 784c040ee6174..8c8edbf24b1c1 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/element_menu/element_menu.component.tsx +++ b/x-pack/plugins/canvas/public/components/workpad_header/element_menu/element_menu.component.tsx @@ -9,8 +9,8 @@ import { sortBy } from 'lodash'; import React, { FunctionComponent, useState } from 'react'; import PropTypes from 'prop-types'; import { EuiContextMenu, EuiIcon, EuiContextMenuPanelItemDescriptor } from '@elastic/eui'; +import { ToolbarPopover } from '@kbn/shared-ux-button-toolbar'; import { i18n } from '@kbn/i18n'; -import { PrimaryActionPopover } from '@kbn/presentation-util-plugin/public'; import { getId } from '../../../lib/get_id'; import { CONTEXT_MENU_TOP_BORDER_CLASSNAME } from '../../../../common/lib'; import { ElementSpec } from '../../../../types'; @@ -203,7 +203,8 @@ export const ElementMenu: FunctionComponent = ({ elements, addElement }) return ( <> - = ({ elements, addElement }) panels={flattenPanelTree(getPanelTree(closePopover))} /> )} - + {isAssetModalVisible ? : null} {isSavedElementsModalVisible ? : null} diff --git a/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.component.tsx b/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.component.tsx index 5bba9ce3895f5..ddac56a5a7731 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.component.tsx +++ b/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.component.tsx @@ -12,11 +12,8 @@ import { Shortcuts } from 'react-shortcuts'; import { EuiFlexItem, EuiFlexGroup, EuiButtonIcon, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { - AddFromLibraryButton, - QuickButtonGroup, - SolutionToolbar, -} from '@kbn/presentation-util-plugin/public'; +import { AddFromLibraryButton, IconButtonGroup, Toolbar } from '@kbn/shared-ux-button-toolbar'; + import { getElementStrings } from '../../../i18n'; import { CommitFn, ElementSpec } from '../../../types'; import { ToolTipShortcut } from '../tool_tip_shortcut'; @@ -31,6 +28,10 @@ import { LabsControl } from './labs_control'; import { EditorMenu } from './editor_menu'; const strings = { + getQuickCreateButtonGroupLegend: () => + i18n.translate('xpack.canvas.workpadHeader.quickCreateButtonGroupLegend', { + defaultMessage: 'Shortcuts to popular element types', + }), getFullScreenButtonAriaLabel: () => i18n.translate('xpack.canvas.workpadHeader.fullscreenButtonAriaLabel', { defaultMessage: 'View fullscreen', @@ -142,34 +143,35 @@ export const WorkpadHeader: FC = ({ const quickButtons = [ { iconType: 'visText', - createType: elementStrings.markdown.displayName, - onClick: createElement('markdown'), + label: elementStrings.markdown.displayName, + onClick: () => createElement('markdown'), }, { iconType: 'node', - createType: elementStrings.shape.displayName, - onClick: createElement('shape'), + label: elementStrings.shape.displayName, + onClick: () => createElement('shape'), }, { iconType: 'image', - createType: elementStrings.image.displayName, - onClick: createElement('image'), + label: elementStrings.image.displayName, + onClick: () => createElement('image'), }, ]; return ( <> - + {isWriteable && ( - + {{ - primaryActionButton: , - quickButtonGroup: , + primaryButton: , + iconButtonGroup: ( + + ), extraButtons: [ = ({ , ], }} - + )} diff --git a/x-pack/plugins/canvas/tsconfig.json b/x-pack/plugins/canvas/tsconfig.json index ae50b56a27bed..ef10a58a1e65b 100644 --- a/x-pack/plugins/canvas/tsconfig.json +++ b/x-pack/plugins/canvas/tsconfig.json @@ -78,6 +78,7 @@ "@kbn/flot-charts", "@kbn/shared-ux-router", "@kbn/babel-register", + "@kbn/shared-ux-button-toolbar", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.test.tsx b/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.test.tsx index c454a864d9af5..cb2b501ed4041 100644 --- a/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.test.tsx +++ b/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.test.tsx @@ -111,7 +111,10 @@ const usePostPushToServiceMock = usePostPushToService as jest.Mock; const useGetCaseConnectorsMock = useGetCaseConnectors as jest.Mock; const useGetCaseUsersMock = useGetCaseUsers as jest.Mock; -describe('Case View Page activity tab', () => { +// FLAKY: https://github.com/elastic/kibana/issues/151979 +// FLAKY: https://github.com/elastic/kibana/issues/151980 +// FLAKY: https://github.com/elastic/kibana/issues/151981 +describe.skip('Case View Page activity tab', () => { const caseConnectors = getCaseConnectorsMockResponse(); beforeAll(() => { diff --git a/x-pack/plugins/cases/public/components/configure_cases/index.tsx b/x-pack/plugins/cases/public/components/configure_cases/index.tsx index e44f61315641c..0f1ee58680129 100644 --- a/x-pack/plugins/cases/public/components/configure_cases/index.tsx +++ b/x-pack/plugins/cases/public/components/configure_cases/index.tsx @@ -94,6 +94,20 @@ export const ConfigureCases: React.FC = React.memo(() => { [refetchActionTypes, refetchCaseConfigure, refetchConnectors, setEditedConnectorItem] ); + const onConnectorCreated = useCallback( + async (createdConnector) => { + const caseConnector = normalizeActionConnector(createdConnector); + + await persistCaseConfigure({ + connector: caseConnector, + closureType, + }); + onConnectorUpdated(createdConnector); + setConnector(caseConnector); + }, + [onConnectorUpdated, closureType, setConnector, persistCaseConfigure] + ); + const isLoadingAny = isLoadingConnectors || persistLoading || loadingCaseConfigure || isLoadingActionTypes; const updateConnectorDisabled = isLoadingAny || !connectorIsValid || connector.id === 'none'; @@ -168,7 +182,7 @@ export const ConfigureCases: React.FC = React.memo(() => { ? triggersActionsUi.getAddConnectorFlyout({ onClose: onCloseAddFlyout, featureId: CasesConnectorFeatureId, - onConnectorCreated: onConnectorUpdated, + onConnectorCreated, }) : null, // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/x-pack/plugins/cases/public/components/description/description_wrapper.test.tsx b/x-pack/plugins/cases/public/components/description/description_wrapper.test.tsx index b4fb76b8dc94b..40fcf91ffe1fa 100644 --- a/x-pack/plugins/cases/public/components/description/description_wrapper.test.tsx +++ b/x-pack/plugins/cases/public/components/description/description_wrapper.test.tsx @@ -33,7 +33,8 @@ jest.mock('../../common/lib/kibana'); const useUpdateCommentMock = useUpdateComment as jest.Mock; const patchComment = jest.fn(); -describe(`DescriptionWrapper`, () => { +// FLAKY: +describe.skip(`DescriptionWrapper`, () => { const sampleData = { content: 'what a great comment update', }; diff --git a/x-pack/plugins/cases/public/components/markdown_editor/plugins/lens/plugin.tsx b/x-pack/plugins/cases/public/components/markdown_editor/plugins/lens/plugin.tsx index 71750bbc2a515..ae88e17637627 100644 --- a/x-pack/plugins/cases/public/components/markdown_editor/plugins/lens/plugin.tsx +++ b/x-pack/plugins/cases/public/components/markdown_editor/plugins/lens/plugin.tsx @@ -28,11 +28,11 @@ import styled from 'styled-components'; import type { TypedLensByValueInput } from '@kbn/lens-plugin/public'; import type { EmbeddablePackageState } from '@kbn/embeddable-plugin/public'; +import { SavedObjectFinderUi } from '@kbn/saved-objects-plugin/public'; import { useKibana } from '../../../../common/lib/kibana'; import { DRAFT_COMMENT_STORAGE_ID, ID } from './constants'; import { CommentEditorContext } from '../../context'; import { ModalContainer } from './modal_container'; -import { SavedObjectFinderUi } from './saved_objects_finder'; import { useLensDraftComment } from './use_lens_draft_comment'; import { VISUALIZATION } from './translations'; import { useIsMainApplication } from '../../../../common/hooks'; @@ -72,7 +72,7 @@ const LensEditorComponent: LensEuiMarkdownEditorUiPlugin['editor'] = ({ embeddable, lens, storage, - savedObjects, + http, uiSettings, data: { query: { @@ -362,9 +362,8 @@ const LensEditorComponent: LensEuiMarkdownEditorUiPlugin['editor'] = ({ savedObjectMetaData={savedObjectMetaData} fixedPageSize={10} uiSettings={uiSettings} - savedObjects={savedObjects} + http={http} euiFieldSearchProps={euiFieldSearchProps} - // @ts-expect-error update types euiFormRowProps={euiFormRowProps} /> diff --git a/x-pack/plugins/cases/public/components/markdown_editor/plugins/lens/saved_objects_finder.tsx b/x-pack/plugins/cases/public/components/markdown_editor/plugins/lens/saved_objects_finder.tsx deleted file mode 100644 index 5b96ea377fc74..0000000000000 --- a/x-pack/plugins/cases/public/components/markdown_editor/plugins/lens/saved_objects_finder.tsx +++ /dev/null @@ -1,555 +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. - */ - -// TODO: merge with src/plugins/saved_objects/public/finder/saved_object_finder.tsx - -import { debounce } from 'lodash'; -import PropTypes from 'prop-types'; -import React from 'react'; - -import type { EuiFieldSearchProps, IconType, EuiFormRowProps } from '@elastic/eui'; -import { - EuiContextMenuItem, - EuiContextMenuPanel, - EuiEmptyPrompt, - EuiFieldSearch, - EuiFilterButton, - EuiFilterGroup, - EuiFlexGroup, - EuiFlexItem, - EuiListGroup, - EuiListGroupItem, - EuiLoadingSpinner, - EuiPagination, - EuiPopover, - EuiSpacer, - EuiTablePagination, - EuiFormRow, -} from '@elastic/eui'; -import type { Direction } from '@elastic/eui/src/services/sort/sort_direction'; -import { i18n } from '@kbn/i18n'; - -import type { SimpleSavedObject, CoreStart } from '@kbn/core/public'; - -import { LISTING_LIMIT_SETTING } from '@kbn/saved-objects-plugin/public'; - -export interface SavedObjectMetaData { - type: string; - name: string; - getIconForSavedObject(savedObject: SimpleSavedObject): IconType; - getTooltipForSavedObject?(savedObject: SimpleSavedObject): string; - showSavedObject?(savedObject: SimpleSavedObject): boolean; - getSavedObjectSubType?(savedObject: SimpleSavedObject): string; - includeFields?: string[]; -} - -interface FinderAttributes { - title?: string; - name?: string; - type: string; -} - -interface SavedObjectFinderState { - items: Array<{ - title: string | null; - name: string | null; - id: SimpleSavedObject['id']; - type: SimpleSavedObject['type']; - savedObject: SimpleSavedObject; - }>; - query: string; - isFetchingItems: boolean; - page: number; - perPage: number; - sortDirection?: Direction; - sortOpen: boolean; - filterOpen: boolean; - filteredTypes: string[]; -} - -interface BaseSavedObjectFinder { - onChoose?: ( - id: SimpleSavedObject['id'], - type: SimpleSavedObject['type'], - name: string, - savedObject: SimpleSavedObject - ) => void; - noItemsMessage?: React.ReactNode; - savedObjectMetaData: Array>; - showFilter?: boolean; - euiFormRowProps?: EuiFormRowProps; - euiFieldSearchProps?: EuiFieldSearchProps; -} - -interface SavedObjectFinderFixedPage extends BaseSavedObjectFinder { - initialPageSize?: undefined; - fixedPageSize: number; -} - -interface SavedObjectFinderInitialPageSize extends BaseSavedObjectFinder { - initialPageSize?: 5 | 10 | 15 | 25; - fixedPageSize?: undefined; -} - -export type SavedObjectFinderProps = SavedObjectFinderFixedPage | SavedObjectFinderInitialPageSize; - -export type SavedObjectFinderUiProps = { - savedObjects: CoreStart['savedObjects']; - uiSettings: CoreStart['uiSettings']; -} & SavedObjectFinderProps; - -// TODO: Fix this manually. Issue #123375 -// eslint-disable-next-line react/display-name -export class SavedObjectFinderUi extends React.Component< - SavedObjectFinderUiProps, - SavedObjectFinderState -> { - public static propTypes = { - onChoose: PropTypes.func, - noItemsMessage: PropTypes.node, - savedObjectMetaData: PropTypes.array.isRequired, - initialPageSize: PropTypes.oneOf([5, 10, 15, 25]), - fixedPageSize: PropTypes.number, - showFilter: PropTypes.bool, - euiFormRowProps: PropTypes.object, - euiFieldSearchProps: PropTypes.object, - }; - - private isComponentMounted: boolean = false; - - private debouncedFetch = debounce(async (query: string) => { - const metaDataMap = this.getSavedObjectMetaDataMap(); - - const fields = Object.values(metaDataMap) - .map((metaData) => metaData.includeFields || []) - .reduce((allFields, currentFields) => allFields.concat(currentFields), ['title', 'name']); - - const perPage = this.props.uiSettings.get(LISTING_LIMIT_SETTING); - const resp = await this.props.savedObjects.client.find({ - type: Object.keys(metaDataMap), - fields: [...new Set(fields)], - search: query ? `${query}*` : undefined, - page: 1, - perPage, - searchFields: ['title^3', 'description'], - defaultSearchOperator: 'AND', - }); - - resp.savedObjects = resp.savedObjects.filter((savedObject) => { - const metaData = metaDataMap[savedObject.type]; - if (metaData.showSavedObject) { - return metaData.showSavedObject(savedObject); - } else { - return true; - } - }); - - if (!this.isComponentMounted) { - return; - } - - // We need this check to handle the case where search results come back in a different - // order than they were sent out. Only load results for the most recent search. - if (query === this.state.query) { - this.setState({ - isFetchingItems: false, - page: 0, - items: resp.savedObjects.map((savedObject) => { - const { - attributes: { name, title }, - id, - type, - } = savedObject; - const titleToUse = typeof title === 'string' ? title : ''; - const nameToUse = name && typeof name === 'string' ? name : titleToUse; - return { - title: titleToUse, - name: nameToUse, - id, - type, - savedObject, - }; - }), - }); - } - }, 300); - - constructor(props: SavedObjectFinderUiProps) { - super(props); - - this.state = { - items: [], - isFetchingItems: false, - page: 0, - perPage: props.initialPageSize || props.fixedPageSize || 10, - query: '', - filterOpen: false, - filteredTypes: [], - sortOpen: false, - }; - } - - public componentWillUnmount() { - this.isComponentMounted = false; - this.debouncedFetch.cancel(); - } - - public componentDidMount() { - this.isComponentMounted = true; - this.fetchItems(); - } - - public render() { - return ( - <> - {this.renderSearchBar()} - {this.renderListing()} - - ); - } - - private getSavedObjectMetaDataMap(): Record { - return this.props.savedObjectMetaData.reduce( - (map, metaData) => ({ ...map, [metaData.type]: metaData }), - {} - ); - } - - private getPageCount() { - return Math.ceil( - (this.state.filteredTypes.length === 0 - ? this.state.items.length - : this.state.items.filter( - (item) => - this.state.filteredTypes.length === 0 || this.state.filteredTypes.includes(item.type) - ).length) / this.state.perPage - ); - } - - // server-side paging not supported - // 1) saved object client does not support sorting by title because title is only mapped as analyzed - // 2) can not search on anything other than title because all other fields are stored in opaque JSON strings, - // for example, visualizations need to be search by isLab but this is not possible in Elasticsearch side - // with the current mappings - private getPageOfItems = () => { - // do not sort original list to preserve elasticsearch ranking order - const items = this.state.items.slice(); - const { sortDirection } = this.state; - - if (sortDirection || !this.state.query) { - items.sort(({ title: titleA }, { title: titleB }) => { - let order = 1; - if (sortDirection === 'desc') { - order = -1; - } - return order * (titleA || '').toLowerCase().localeCompare((titleB || '').toLowerCase()); - }); - } - - // If begin is greater than the length of the sequence, an empty array is returned. - const startIndex = this.state.page * this.state.perPage; - // If end is greater than the length of the sequence, slice extracts through to the end of the sequence (arr.length). - const lastIndex = startIndex + this.state.perPage; - return items - .filter( - (item) => - this.state.filteredTypes.length === 0 || this.state.filteredTypes.includes(item.type) - ) - .slice(startIndex, lastIndex); - }; - - private fetchItems = () => { - this.setState( - { - isFetchingItems: true, - }, - this.debouncedFetch.bind(null, this.state.query) - ); - }; - - private getAvailableSavedObjectMetaData() { - const typesInItems = new Set(); - this.state.items.forEach((item) => { - typesInItems.add(item.type); - }); - return this.props.savedObjectMetaData.filter((metaData) => typesInItems.has(metaData.type)); - } - - private getSortOptions() { - const sortOptions = [ - { - this.setState({ - sortDirection: 'asc', - }); - }} - > - {i18n.translate('xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.sortAsc', { - defaultMessage: 'Ascending', - })} - , - { - this.setState({ - sortDirection: 'desc', - }); - }} - > - {i18n.translate('xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.sortDesc', { - defaultMessage: 'Descending', - })} - , - ]; - if (this.state.query) { - sortOptions.push( - { - this.setState({ - sortDirection: undefined, - }); - }} - > - {i18n.translate('xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.sortAuto', { - defaultMessage: 'Best match', - })} - - ); - } - return sortOptions; - } - - private renderSearchBar() { - const availableSavedObjectMetaData = this.getAvailableSavedObjectMetaData(); - - return ( - - - - { - this.setState( - { - query: e.target.value, - }, - this.fetchItems - ); - }} - data-test-subj="savedObjectFinderSearchInput" - isLoading={this.state.isFetchingItems} - {...(this.props.euiFieldSearchProps || {})} - /> - - - - this.setState({ sortOpen: false })} - button={ - - this.setState(({ sortOpen }) => ({ - sortOpen: !sortOpen, - })) - } - iconType="arrowDown" - isSelected={this.state.sortOpen} - data-test-subj="savedObjectFinderSortButton" - > - {i18n.translate( - 'xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.sortButtonLabel', - { - defaultMessage: 'Sort', - } - )} - - } - > - - - {this.props.showFilter && ( - this.setState({ filterOpen: false })} - button={ - - this.setState(({ filterOpen }) => ({ - filterOpen: !filterOpen, - })) - } - iconType="arrowDown" - data-test-subj="savedObjectFinderFilterButton" - isSelected={this.state.filterOpen} - numFilters={this.props.savedObjectMetaData.length} - hasActiveFilters={this.state.filteredTypes.length > 0} - numActiveFilters={this.state.filteredTypes.length} - > - {i18n.translate( - 'xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.filterButtonLabel', - { - defaultMessage: 'Types', - } - )} - - } - > - ( - { - this.setState(({ filteredTypes }) => ({ - filteredTypes: filteredTypes.includes(metaData.type) - ? filteredTypes.filter((t) => t !== metaData.type) - : [...filteredTypes, metaData.type], - page: 0, - })); - }} - > - {metaData.name} - - ))} - /> - - )} - - - {this.props.children ? ( - {this.props.children} - ) : null} - - - ); - } - - private renderListing() { - const items = this.state.items.length === 0 ? [] : this.getPageOfItems(); - const { onChoose, savedObjectMetaData } = this.props; - - return ( - <> - {this.state.isFetchingItems && this.state.items.length === 0 && ( - - - - - - - )} - {items.length > 0 ? ( - <> - - - {items.map((item) => { - const currentSavedObjectMetaData = savedObjectMetaData.find( - (metaData) => metaData.type === item.type - ); - - if (currentSavedObjectMetaData == null) { - return null; - } - - const fullName = currentSavedObjectMetaData.getTooltipForSavedObject - ? currentSavedObjectMetaData.getTooltipForSavedObject(item.savedObject) - : `${item.name} (${currentSavedObjectMetaData.name})`; - - const iconType = ( - currentSavedObjectMetaData || - ({ - getIconForSavedObject: () => 'document', - } as Pick, 'getIconForSavedObject'>) - ).getIconForSavedObject(item.savedObject); - - return ( - { - onChoose(item.id, item.type, fullName, item.savedObject); - } - : undefined - } - title={fullName} - data-test-subj={`savedObjectTitle${(item.title || '').split(' ').join('-')}`} - /> - ); - })} - - - ) : ( - !this.state.isFetchingItems && - )} - {this.getPageCount() > 1 && - (this.props.fixedPageSize ? ( - { - this.setState({ - page, - }); - }} - /> - ) : ( - { - this.setState({ - page, - }); - }} - onChangeItemsPerPage={(perPage) => { - this.setState({ - perPage, - }); - }} - itemsPerPage={this.state.perPage} - itemsPerPageOptions={[5, 10, 15, 25]} - /> - ))} - - ); - } -} diff --git a/x-pack/plugins/cases/server/saved_object_types/cases.ts b/x-pack/plugins/cases/server/saved_object_types/cases.ts index e32aa462d7121..1d70808f14db2 100644 --- a/x-pack/plugins/cases/server/saved_object_types/cases.ts +++ b/x-pack/plugins/cases/server/saved_object_types/cases.ts @@ -26,6 +26,7 @@ export const createCaseSavedObjectType = ( namespaceType: 'multiple-isolated', convertToMultiNamespaceTypeVersion: '8.0.0', mappings: { + dynamic: false, properties: { assignees: { properties: { diff --git a/x-pack/plugins/cases/server/saved_object_types/comments.ts b/x-pack/plugins/cases/server/saved_object_types/comments.ts index c795b7d960e61..1d02a26f732c8 100644 --- a/x-pack/plugins/cases/server/saved_object_types/comments.ts +++ b/x-pack/plugins/cases/server/saved_object_types/comments.ts @@ -20,6 +20,7 @@ export const createCaseCommentSavedObjectType = ({ namespaceType: 'multiple-isolated', convertToMultiNamespaceTypeVersion: '8.0.0', mappings: { + dynamic: false, properties: { comment: { type: 'text', @@ -32,115 +33,34 @@ export const createCaseCommentSavedObjectType = ({ }, actions: { properties: { - targets: { - type: 'nested', - properties: { - hostname: { type: 'keyword' }, - endpointId: { type: 'keyword' }, - }, - }, type: { type: 'keyword' }, }, }, alertId: { type: 'keyword', }, - index: { - type: 'keyword', - }, created_at: { type: 'date', }, created_by: { properties: { - full_name: { - type: 'keyword', - }, username: { type: 'keyword', }, - email: { - type: 'keyword', - }, - profile_uid: { - type: 'keyword', - }, - }, - }, - externalReferenceId: { - type: 'keyword', - }, - externalReferenceStorage: { - dynamic: false, - properties: { - // externalReferenceStorage.type - type: { - type: 'keyword', - }, }, }, externalReferenceAttachmentTypeId: { type: 'keyword', }, - externalReferenceMetadata: { - dynamic: false, - properties: {}, - }, persistableStateAttachmentTypeId: { type: 'keyword', }, - persistableStateAttachmentState: { - dynamic: false, - properties: {}, - }, pushed_at: { type: 'date', }, - pushed_by: { - properties: { - username: { - type: 'keyword', - }, - full_name: { - type: 'keyword', - }, - email: { - type: 'keyword', - }, - profile_uid: { - type: 'keyword', - }, - }, - }, - rule: { - properties: { - id: { - type: 'keyword', - }, - name: { - type: 'keyword', - }, - }, - }, updated_at: { type: 'date', }, - updated_by: { - properties: { - username: { - type: 'keyword', - }, - full_name: { - type: 'keyword', - }, - email: { - type: 'keyword', - }, - profile_uid: { - type: 'keyword', - }, - }, - }, }, }, migrations: () => createCommentsMigrations(migrationDeps), diff --git a/x-pack/plugins/cases/server/saved_object_types/configure.ts b/x-pack/plugins/cases/server/saved_object_types/configure.ts index 2ee1e3458c647..879640c4b6e40 100644 --- a/x-pack/plugins/cases/server/saved_object_types/configure.ts +++ b/x-pack/plugins/cases/server/saved_object_types/configure.ts @@ -15,71 +15,17 @@ export const caseConfigureSavedObjectType: SavedObjectsType = { namespaceType: 'multiple-isolated', convertToMultiNamespaceTypeVersion: '8.0.0', mappings: { + dynamic: false, properties: { created_at: { type: 'date', }, - created_by: { - properties: { - email: { - type: 'keyword', - }, - username: { - type: 'keyword', - }, - full_name: { - type: 'keyword', - }, - profile_uid: { - type: 'keyword', - }, - }, - }, - connector: { - properties: { - name: { - type: 'text', - }, - type: { - type: 'keyword', - }, - fields: { - properties: { - key: { - type: 'text', - }, - value: { - type: 'text', - }, - }, - }, - }, - }, closure_type: { type: 'keyword', }, owner: { type: 'keyword', }, - updated_at: { - type: 'date', - }, - updated_by: { - properties: { - email: { - type: 'keyword', - }, - username: { - type: 'keyword', - }, - full_name: { - type: 'keyword', - }, - profile_uid: { - type: 'keyword', - }, - }, - }, }, }, migrations: configureMigrations, diff --git a/x-pack/plugins/cases/server/saved_object_types/connector_mappings.ts b/x-pack/plugins/cases/server/saved_object_types/connector_mappings.ts index 7d89b090847d3..1335570e8a132 100644 --- a/x-pack/plugins/cases/server/saved_object_types/connector_mappings.ts +++ b/x-pack/plugins/cases/server/saved_object_types/connector_mappings.ts @@ -15,20 +15,8 @@ export const caseConnectorMappingsSavedObjectType: SavedObjectsType = { namespaceType: 'multiple-isolated', convertToMultiNamespaceTypeVersion: '8.0.0', mappings: { + dynamic: false, properties: { - mappings: { - properties: { - source: { - type: 'keyword', - }, - target: { - type: 'keyword', - }, - action_type: { - type: 'keyword', - }, - }, - }, owner: { type: 'keyword', }, diff --git a/x-pack/plugins/cases/server/saved_object_types/user_actions.ts b/x-pack/plugins/cases/server/saved_object_types/user_actions.ts index 067ecaaf8fe1e..d8c442b39dab1 100644 --- a/x-pack/plugins/cases/server/saved_object_types/user_actions.ts +++ b/x-pack/plugins/cases/server/saved_object_types/user_actions.ts @@ -18,6 +18,7 @@ export const createCaseUserActionSavedObjectType = ( namespaceType: 'multiple-isolated', convertToMultiNamespaceTypeVersion: '8.0.0', mappings: { + dynamic: false, properties: { action: { type: 'keyword', @@ -27,18 +28,9 @@ export const createCaseUserActionSavedObjectType = ( }, created_by: { properties: { - email: { - type: 'keyword', - }, username: { type: 'keyword', }, - full_name: { - type: 'keyword', - }, - profile_uid: { - type: 'keyword', - }, }, }, payload: { diff --git a/x-pack/plugins/cloud_defend/public/components/control_general_view/translations.ts b/x-pack/plugins/cloud_defend/public/components/control_general_view/translations.ts index 26c77aa6aa74b..b886bd1c4d68a 100644 --- a/x-pack/plugins/cloud_defend/public/components/control_general_view/translations.ts +++ b/x-pack/plugins/cloud_defend/public/components/control_general_view/translations.ts @@ -21,7 +21,7 @@ export const selectors = i18n.translate('xpack.cloudDefend.controlSelectors', { }); export const selectorsHelp = i18n.translate('xpack.cloudDefend.controlSelectorsHelp', { - defaultMessage: 'Create selectors to match on activities that should be blocked or alerted.', + defaultMessage: 'Create selectors to match on operations that should be blocked or alerted.', }); export const responses = i18n.translate('xpack.cloudDefend.controlResponses', { @@ -99,6 +99,22 @@ export const errorValueLengthExceeded = i18n.translate( } ); +export const getConditionHelpLabel = (prop: string) => { + switch (prop) { + case ControlSelectorCondition.ignoreVolumeMounts: + return i18n.translate('xpack.cloudDefend.ignoreVolumeMountsHelp', { + defaultMessage: 'Ignore operations on all volume mounts.', + }); + case ControlSelectorCondition.ignoreVolumeFiles: + return i18n.translate('xpack.cloudDefend.ignoreVolumeFilesHelp', { + defaultMessage: + 'Ignore operations on file mounts only. e.g mounted files, configMaps, secrets etc...', + }); + default: + return ''; + } +}; + export const getConditionLabel = (prop: string) => { switch (prop) { case ControlSelectorCondition.operation: @@ -117,6 +133,14 @@ export const getConditionLabel = (prop: string) => { return i18n.translate('xpack.cloudDefend.targetFilePath', { defaultMessage: 'Target file path', }); + case ControlSelectorCondition.ignoreVolumeFiles: + return i18n.translate('xpack.cloudDefend.ignoreVolumeFiles', { + defaultMessage: 'Ignore volume files', + }); + case ControlSelectorCondition.ignoreVolumeMounts: + return i18n.translate('xpack.cloudDefend.ignoreVolumeMounts', { + defaultMessage: 'Ignore volume mounts', + }); case ControlSelectorCondition.orchestratorClusterId: return i18n.translate('xpack.cloudDefend.orchestratorClusterId', { defaultMessage: 'Orchestrator cluster ID', @@ -141,5 +165,7 @@ export const getConditionLabel = (prop: string) => { return i18n.translate('xpack.cloudDefend.orchestratorResourceType', { defaultMessage: 'Orchestrator resource type', }); + default: + return ''; } }; diff --git a/x-pack/plugins/cloud_defend/public/components/control_general_view_selector/index.test.tsx b/x-pack/plugins/cloud_defend/public/components/control_general_view_selector/index.test.tsx index 35f1a418725a0..77273cc1b2ba1 100644 --- a/x-pack/plugins/cloud_defend/public/components/control_general_view_selector/index.test.tsx +++ b/x-pack/plugins/cloud_defend/public/components/control_general_view_selector/index.test.tsx @@ -118,6 +118,21 @@ describe('', () => { expect(updatedOptions[0]).not.toHaveTextContent('containerImageName'); }); + it('allows the user add boolean type conditions', async () => { + const { getByTestId, rerender } = render(); + const addConditionBtn = getByTestId('cloud-defend-btnaddselectorcondition'); + + userEvent.click(addConditionBtn); + + const addIgnoreVolumeMounts = getByTestId('cloud-defend-addmenu-ignoreVolumeMounts'); + + await waitFor(() => userEvent.click(addIgnoreVolumeMounts)); + + const updatedSelector: ControlSelector = { ...onChange.mock.calls[0][0] }; + rerender(); + expect(updatedSelector.ignoreVolumeMounts).toBeTruthy(); + }); + it('shows an error if no conditions are added', async () => { const { getByText, getByTestId, rerender } = render(); diff --git a/x-pack/plugins/cloud_defend/public/components/control_general_view_selector/index.tsx b/x-pack/plugins/cloud_defend/public/components/control_general_view_selector/index.tsx index 1bcedbe13cc4a..bc42466c63329 100644 --- a/x-pack/plugins/cloud_defend/public/components/control_general_view_selector/index.tsx +++ b/x-pack/plugins/cloud_defend/public/components/control_general_view_selector/index.tsx @@ -19,6 +19,7 @@ import { EuiSpacer, EuiFlexGroup, EuiFlexItem, + EuiText, } from '@elastic/eui'; import { useStyles } from './styles'; import { @@ -26,6 +27,7 @@ import { ControlFormErrorMap, ControlSelectorCondition, ControlSelectorConditionUIOptionsMap, + ControlSelectorBooleanConditions, ControlSelector, } from '../../types'; import * as i18n from '../control_general_view/translations'; @@ -36,6 +38,104 @@ import { MAX_FILE_PATH_VALUE_LENGTH_BYTES, } from '../../common/constants'; +interface ConditionProps { + label: string; + prop: string; + onRemoveCondition(prop: string): void; +} + +interface StringArrayConditionProps extends ConditionProps { + selector: ControlSelector; + errorMap: ControlFormErrorMap; + onAddValueToCondition(prop: string, value: string): void; + onChangeStringArrayCondition(prop: string, value: string[]): void; +} + +const BooleanCondition = ({ label, prop, onRemoveCondition }: ConditionProps) => { + return ( + + + + +

    + {i18n.getConditionHelpLabel(prop)} +

    +
    +
    + + onRemoveCondition(prop)} + aria-label="Remove condition" + data-test-subj={'cloud-defend-btnremovecondition-' + prop} + /> + +
    +
    + ); +}; + +const StringArrayCondition = ({ + label, + prop, + selector, + errorMap, + onRemoveCondition, + onAddValueToCondition, + onChangeStringArrayCondition, +}: StringArrayConditionProps) => { + const values = selector[prop as keyof ControlSelector] as string[]; + const selectedOptions = + values?.map((option) => { + return { label: option, value: option }; + }) || []; + + const restrictedValues = ControlSelectorConditionUIOptionsMap[prop]?.values; + + return ( + + + + onAddValueToCondition(prop, searchValue) + : undefined + } + selectedOptions={selectedOptions} + options={ + restrictedValues + ? restrictedValues.map((value: string) => ({ label: value, value })) + : selectedOptions + } + onChange={(options) => + onChangeStringArrayCondition(prop, options.map((option) => option.value) as string[]) + } + isClearable + data-test-subj={'cloud-defend-selectorcondition-' + prop} + /> + + + onRemoveCondition(prop)} + aria-label="Remove condition" + data-test-subj={'cloud-defend-btnremovecondition-' + prop} + /> + + + + ); +}; + +/** main component */ export const ControlGeneralViewSelector = ({ selector, selectors, @@ -121,7 +221,7 @@ export const ControlGeneralViewSelector = ({ [errorMap, index, conditionsAdded, onChange, selector, selectors] ); - const onChangeCondition = useCallback( + const onChangeStringArrayCondition = useCallback( (prop: string, values: string[]) => { const updatedSelector = { ...selector, [prop]: values }; const errors = []; @@ -156,12 +256,25 @@ export const ControlGeneralViewSelector = ({ [errorMap, index, conditionsAdded, onChange, selector] ); + const onChangeBooleanCondition = useCallback( + (prop: string, value: boolean) => { + const updatedSelector = { ...selector, [prop]: value }; + + onChange(updatedSelector, index); + }, + [index, onChange, selector] + ); + const onAddCondition = useCallback( (prop: string) => { - onChangeCondition(prop, []); + if (prop in ControlSelectorBooleanConditions) { + onChangeBooleanCondition(prop, true); + } else { + onChangeStringArrayCondition(prop, []); + } closeAddCondition(); }, - [closeAddCondition, onChangeCondition] + [closeAddCondition, onChangeBooleanCondition, onChangeStringArrayCondition] ); const onRemoveCondition = useCallback( @@ -185,10 +298,10 @@ export const ControlGeneralViewSelector = ({ const values = selector[prop as keyof ControlSelector] as string[]; if (values && values.indexOf(value) === -1) { - onChangeCondition(prop, [...values, value]); + onChangeStringArrayCondition(prop, [...values, value]); } }, - [onChangeCondition, selector] + [onChangeStringArrayCondition, selector] ); const errors = useMemo(() => { @@ -270,56 +383,31 @@ export const ControlGeneralViewSelector = ({ {Object.keys(selector).map((prop: string) => { if (['name', 'hasErrors'].indexOf(prop) === -1) { - const values = selector[prop as keyof ControlSelector] as string[]; - const selectedOptions = - values?.map((option) => { - return { label: option, value: option }; - }) || []; - const label = i18n.getConditionLabel(prop); - const restrictedValues = ControlSelectorConditionUIOptionsMap[prop]?.values; - return ( - - - - onAddValueToCondition(prop, searchValue) - : undefined - } - selectedOptions={selectedOptions} - options={ - restrictedValues - ? restrictedValues.map((value: string) => ({ label: value, value })) - : selectedOptions - } - onChange={(options) => - onChangeCondition(prop, options.map((option) => option.value) as string[]) - } - isClearable - data-test-subj={'cloud-defend-selectorcondition-' + prop} - /> - - - onRemoveCondition(prop)} - aria-label="Remove condition" - data-test-subj={'cloud-defend-btnremovecondition-' + prop} - /> - - - - ); + if (prop in ControlSelectorBooleanConditions) { + return ( + + ); + } else { + return ( + + ); + } } })} @@ -345,7 +433,11 @@ export const ControlGeneralViewSelector = ({ size="s" items={remainingProps.map((prop) => { return ( - onAddCondition(prop)}> + onAddCondition(prop)} + > {i18n.getConditionLabel(prop)} ); diff --git a/x-pack/plugins/cloud_defend/public/components/control_yaml_view/hooks/policy_schema.json b/x-pack/plugins/cloud_defend/public/components/control_yaml_view/hooks/policy_schema.json new file mode 100644 index 0000000000000..e3c5a2675dcad --- /dev/null +++ b/x-pack/plugins/cloud_defend/public/components/control_yaml_view/hooks/policy_schema.json @@ -0,0 +1,190 @@ +{ + "$id": "https://elastic.co/cloud-defend/policy-schema.json", + "type": "object", + "required": ["selectors", "responses"], + "additionalProperties": false, + "properties": { + "selectors": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/selector" + } + }, + "responses": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/response" + } + } + }, + "$defs": { + "selector": { + "type": "object", + "required": ["name"], + "additionalProperties": false, + "anyOf": [ + { + "required": ["operation"] + }, + { + "required": ["containerImageName"] + }, + { + "required": ["containerImageTag"] + }, + { + "required": ["targetFilePath"] + }, + { + "required": ["orchestratorClusterId"] + }, + { + "required": ["orchestratorClusterName"] + }, + { + "required": ["orchestratorNamespace"] + }, + { + "required": ["orchestratorResourceLabel"] + }, + { + "required": ["orchestratorResourceName"] + }, + { + "required": ["orchestratorType"] + }, + { + "required": ["ignoreVolumeMounts"] + }, + { + "required": ["ignoreVolumeFiles"] + } + ], + "properties": { + "name": { + "type": "string" + }, + "operation": { + "type": "array", + "minItems": 1, + "items": { + "enum": [ + "createExecutable", + "modifyExecutable", + "createFile", + "modifyFile", + "deleteFile" + ] + } + }, + "containerImageName": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "containerImageTag": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "targetFilePath": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "orchestratorClusterId": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "orchestratorClusterName": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "orchestratorNamespace": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "orchestratorResourceLabel": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "orchestratorResourceName": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "orchestratorType": { + "type": "array", + "minItems": 1, + "items": { + "enum": ["kubernetes"] + } + }, + "ignoreVolumeMounts": { + "type": "boolean", + "description": "Ignore all volume mounts. e.g directories, files, configMaps, secrets etc...\nNote: should not be used with ignoreVolumeFiles" + }, + "ignoreVolumeFiles": { + "type": "boolean", + "description": "Ignore file mounts. e.g files, configMaps, secrets\nNote: should not be used with ignoreVolumeMounts" + } + }, + "dependencies": { + "ignoreVolumeMounts": { + "not": { + "required": ["ignoreVolumeFiles"] + } + } + } + }, + "response": { + "type": "object", + "required": ["match", "actions"], + "additionalProperties": false, + "properties": { + "match": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "actions": { + "type": "array", + "minItems": 1, + "items": { + "enum": ["alert", "block"] + } + } + } + } + } +} diff --git a/x-pack/plugins/cloud_defend/public/components/control_yaml_view/hooks/use_config_model.ts b/x-pack/plugins/cloud_defend/public/components/control_yaml_view/hooks/use_config_model.ts index 36d9628214fbe..359639f2a56cf 100644 --- a/x-pack/plugins/cloud_defend/public/components/control_yaml_view/hooks/use_config_model.ts +++ b/x-pack/plugins/cloud_defend/public/components/control_yaml_view/hooks/use_config_model.ts @@ -8,7 +8,13 @@ import { useMemo } from 'react'; import yaml from 'js-yaml'; import { setDiagnosticsOptions } from 'monaco-yaml'; import { monaco } from '@kbn/monaco'; -import { MAX_CONDITION_VALUE_LENGTH } from '../../../common/constants'; + +/** + * In order to keep this json in sync with https://github.com/elastic/cloud-defend/blob/main/modules/service/policy-schema.json + * Do NOT commit edits to policy_schema.json as part of a PR. Please make the changes in the cloud-defend repo and use the + * make push-policy-schema-kibana command to automate the creation of a PR to sync the changes. + */ +import policySchemaJson from './policy_schema.json'; const { Uri, editor } = monaco; @@ -24,8 +30,25 @@ export const useConfigModel = (configuration: string) => { } }, [configuration]); + // creating a string csv to avoid the next useMemo from re-running regardless of whether + // selector names changed or not. + const selectorNamesCSV = useMemo( + () => json?.selectors?.map((selector: any) => selector.name).join(',') || '', + [json?.selectors] + ); + return useMemo(() => { - const selectorNames = json?.selectors?.map((selector: any) => selector.name) || []; + const schema: any = { ...policySchemaJson }; + + // dynamically setting enum values for response match and exclude properties. + if (schema.$defs.response.properties.match.items) { + const responseProps = schema.$defs.response.properties; + const selectorEnum = { enum: selectorNamesCSV.split(',') }; + responseProps.match.items = selectorEnum; + responseProps.exclude.items = selectorEnum; + } else { + throw new Error('cloud_defend json schema is invalid'); + } setDiagnosticsOptions({ validate: true, @@ -35,112 +58,7 @@ export const useConfigModel = (configuration: string) => { { uri: SCHEMA_URI, fileMatch: [String(modelUri)], - schema: { - type: 'object', - required: ['selectors', 'responses'], - additionalProperties: false, - properties: { - selectors: { - type: 'array', - minItems: 1, - items: { $ref: '#/$defs/selector' }, - }, - responses: { - type: 'array', - minItems: 1, - items: { $ref: '#/$defs/response' }, - }, - }, - $defs: { - selector: { - type: 'object', - required: ['name'], - additionalProperties: false, - anyOf: [ - { required: ['operation'] }, - { required: ['containerImageName'] }, - { required: ['containerImageTag'] }, - { required: ['targetFilePath'] }, - { required: ['orchestratorClusterId'] }, - { required: ['orchestratorClusterName'] }, - { required: ['orchestratorNamespace'] }, - { required: ['orchestratorResourceLabel'] }, - { required: ['orchestratorResourceName'] }, - { required: ['orchestratorType'] }, - ], - properties: { - name: { - type: 'string', - maxLength: MAX_CONDITION_VALUE_LENGTH, - }, - operation: { - type: 'array', - minItems: 1, - items: { enum: ['createExecutable', 'modifyExecutable', 'execMemFd'] }, - }, - containerImageName: { - type: 'array', - minItems: 1, - items: { type: 'string', maxLength: MAX_CONDITION_VALUE_LENGTH }, - }, - containerImageTag: { - type: 'array', - minItems: 1, - items: { type: 'string', maxLength: MAX_CONDITION_VALUE_LENGTH }, - }, - targetFilePath: { - type: 'array', - minItems: 1, - items: { type: 'string', maxLength: MAX_CONDITION_VALUE_LENGTH }, - }, - orchestratorClusterId: { - type: 'array', - minItems: 1, - items: { type: 'string', maxLength: MAX_CONDITION_VALUE_LENGTH }, - }, - orchestratorClusterName: { - type: 'array', - minItems: 1, - items: { type: 'string', maxLength: MAX_CONDITION_VALUE_LENGTH }, - }, - orchestratorNamespace: { - type: 'array', - minItems: 1, - items: { type: 'string', maxLength: MAX_CONDITION_VALUE_LENGTH }, - }, - orchestratorResourceLabel: { - type: 'array', - minItems: 1, - items: { type: 'string', maxLength: MAX_CONDITION_VALUE_LENGTH }, - }, - orchestratorResourceName: { - type: 'array', - minItems: 1, - items: { type: 'string', maxLength: MAX_CONDITION_VALUE_LENGTH }, - }, - orchestratorType: { - type: 'array', - minItems: 1, - items: { enum: ['kubernetes'] }, - }, - }, - }, - response: { - type: 'object', - required: ['match', 'actions'], - additionalProperties: false, - properties: { - match: { type: 'array', minItems: 1, items: { enum: selectorNames } }, - exclude: { type: 'array', items: { enum: selectorNames } }, - actions: { - type: 'array', - minItems: 1, - items: { enum: ['alert', 'block'] }, - }, - }, - }, - }, - }, + schema, }, ], }); @@ -148,9 +66,9 @@ export const useConfigModel = (configuration: string) => { let model = editor.getModel(modelUri); if (model === null) { - model = editor.createModel(configuration, 'yaml', modelUri); + model = editor.createModel('', 'yaml', modelUri); } return model; - }, [configuration, json?.selectors]); + }, [selectorNamesCSV]); }; diff --git a/x-pack/plugins/cloud_defend/public/components/policy_settings/translations.ts b/x-pack/plugins/cloud_defend/public/components/policy_settings/translations.ts index 84fb74351e213..8cb10aa562c06 100644 --- a/x-pack/plugins/cloud_defend/public/components/policy_settings/translations.ts +++ b/x-pack/plugins/cloud_defend/public/components/policy_settings/translations.ts @@ -8,10 +8,10 @@ import { i18n } from '@kbn/i18n'; export const enableControl = i18n.translate('xpack.cloudDefend.enableControl', { - defaultMessage: 'Enable BPF/LSM controls', + defaultMessage: 'Enable drift prevention', }); export const enableControlHelp = i18n.translate('xpack.cloudDefend.enableControlHelp', { defaultMessage: - 'Enables BPF/LSM control mechanism, for use with FIM and container drift prevention.', + 'Toggles enablement of drift prevention policy to alert and/or block file operations.', }); diff --git a/x-pack/plugins/cloud_defend/public/types.ts b/x-pack/plugins/cloud_defend/public/types.ts index 3f1704b2cd07e..801f5ee67891e 100755 --- a/x-pack/plugins/cloud_defend/public/types.ts +++ b/x-pack/plugins/cloud_defend/public/types.ts @@ -30,6 +30,8 @@ export enum ControlSelectorCondition { containerImageName = 'containerImageName', containerImageTag = 'containerImageTag', targetFilePath = 'targetFilePath', + ignoreVolumeFiles = 'ignoreVolumeFiles', + ignoreVolumeMounts = 'ignoreVolumeMounts', orchestratorClusterId = 'orchestratorClusterId', orchestratorClusterName = 'orchestratorClusterName', orchestratorNamespace = 'orchestratorNamespace', @@ -39,6 +41,11 @@ export enum ControlSelectorCondition { orchestratorType = 'orchestratorType', } +export enum ControlSelectorBooleanConditions { + ignoreVolumeFiles = 'ignoreVolumeFiles', + ignoreVolumeMounts = 'ignoreVolumeMounts', +} + export enum ControlSelectorOperation { createExecutable = 'createExecutable', modifyExecutable = 'modifyExecutable', @@ -66,6 +73,8 @@ export interface ControlSelector { containerImageName?: string[]; containerImageTag?: string[]; targetFilePath?: string[]; + ignoreVolumeFiles?: boolean; + ignoreVolumeMounts?: boolean; orchestratorClusterId?: string[]; orchestratorClusterName?: string[]; orchestratorNamespace?: string[]; diff --git a/x-pack/plugins/cloud_defend/tsconfig.json b/x-pack/plugins/cloud_defend/tsconfig.json index 630c1ba061a32..f96b98d8c44dd 100755 --- a/x-pack/plugins/cloud_defend/tsconfig.json +++ b/x-pack/plugins/cloud_defend/tsconfig.json @@ -1,12 +1,14 @@ { "extends": "../../../tsconfig.base.json", "compilerOptions": { - "outDir": "target/types", + "outDir": "target/types" }, "include": [ "common/**/*", "public/**/*", - "../../../typings/**/*" + "../../../typings/**/*", + "server/**/*.json", + "public/**/*.json" ], "kbn_references": [ "@kbn/core", @@ -21,6 +23,6 @@ "@kbn/shared-ux-router" ], "exclude": [ - "target/**/*", + "target/**/*" ] } diff --git a/x-pack/plugins/cloud_security_posture/common/constants.ts b/x-pack/plugins/cloud_security_posture/common/constants.ts index 7cc587df5d499..084d3cca23a36 100644 --- a/x-pack/plugins/cloud_security_posture/common/constants.ts +++ b/x-pack/plugins/cloud_security_posture/common/constants.ts @@ -43,18 +43,25 @@ export const INTERNAL_FEATURE_FLAGS = { export const CSP_RULE_TEMPLATE_SAVED_OBJECT_TYPE = 'csp-rule-template'; -export const CLOUDBEAT_VANILLA = 'cloudbeat/cis_k8s'; // Integration input -export const CLOUDBEAT_EKS = 'cloudbeat/cis_eks'; // Integration input -export const CLOUDBEAT_AWS = 'cloudbeat/cis_aws'; // Integration input -export const CLOUDBEAT_GCP = 'cloudbeat/cis_gcp'; // Integration input -export const CLOUDBEAT_AZURE = 'cloudbeat/cis_azure'; // Integration input +export const CLOUDBEAT_VANILLA = 'cloudbeat/cis_k8s'; +export const CLOUDBEAT_EKS = 'cloudbeat/cis_eks'; +export const CLOUDBEAT_AWS = 'cloudbeat/cis_aws'; +export const CLOUDBEAT_GCP = 'cloudbeat/cis_gcp'; +export const CLOUDBEAT_AZURE = 'cloudbeat/cis_azure'; +export const CLOUDBEAT_VULN_MGMT_AWS = 'cloudbeat/vuln_mgmt_aws'; export const KSPM_POLICY_TEMPLATE = 'kspm'; export const CSPM_POLICY_TEMPLATE = 'cspm'; -export const SUPPORTED_POLICY_TEMPLATES = [KSPM_POLICY_TEMPLATE, CSPM_POLICY_TEMPLATE] as const; +export const VULN_MGMT_POLICY_TEMPLATE = 'vuln_mgmt'; +export const SUPPORTED_POLICY_TEMPLATES = [ + KSPM_POLICY_TEMPLATE, + CSPM_POLICY_TEMPLATE, + VULN_MGMT_POLICY_TEMPLATE, +] as const; export const SUPPORTED_CLOUDBEAT_INPUTS = [ CLOUDBEAT_VANILLA, CLOUDBEAT_EKS, CLOUDBEAT_AWS, CLOUDBEAT_GCP, CLOUDBEAT_AZURE, + CLOUDBEAT_VULN_MGMT_AWS, ] as const; diff --git a/x-pack/plugins/cloud_security_posture/common/types.ts b/x-pack/plugins/cloud_security_posture/common/types.ts index 4bf34b201333f..0e029e17a7024 100644 --- a/x-pack/plugins/cloud_security_posture/common/types.ts +++ b/x-pack/plugins/cloud_security_posture/common/types.ts @@ -77,7 +77,7 @@ interface BaseCspSetupStatus { installedPackagePolicies: number; healthyAgents: number; isPluginInitialized: boolean; - installedPolicyTemplates: PosturePolicyTemplate[]; + installedPolicyTemplates: CloudSecurityPolicyTemplate[]; } interface CspSetupNotInstalledStatus extends BaseCspSetupStatus { @@ -106,4 +106,5 @@ export type BenchmarkName = CspRuleTemplateMetadata['benchmark']['name']; // Fleet Integration types export type PostureInput = typeof SUPPORTED_CLOUDBEAT_INPUTS[number]; -export type PosturePolicyTemplate = typeof SUPPORTED_POLICY_TEMPLATES[number]; +export type CloudSecurityPolicyTemplate = typeof SUPPORTED_POLICY_TEMPLATES[number]; +export type PosturePolicyTemplate = Extract; diff --git a/x-pack/plugins/cloud_security_posture/public/common/constants.ts b/x-pack/plugins/cloud_security_posture/public/common/constants.ts index d6218c451fd72..41e30e16259cd 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/constants.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/constants.ts @@ -7,13 +7,17 @@ import { i18n } from '@kbn/i18n'; import { euiThemeVars } from '@kbn/ui-theme'; -import type { PosturePolicyTemplate, PostureInput } from '../../common/types'; +import type { CloudSecurityPolicyTemplate, PostureInput } from '../../common/types'; import { CLOUDBEAT_EKS, CLOUDBEAT_VANILLA, CLOUDBEAT_AWS, CLOUDBEAT_GCP, CLOUDBEAT_AZURE, + CLOUDBEAT_VULN_MGMT_AWS, + KSPM_POLICY_TEMPLATE, + CSPM_POLICY_TEMPLATE, + VULN_MGMT_POLICY_TEMPLATE, } from '../../common/constants'; import eksLogo from '../assets/icons/cis_eks_logo.svg'; @@ -33,9 +37,12 @@ export const LOCAL_STORAGE_PAGE_SIZE_RULES_KEY = 'cloudPosture:rules:pageSize'; export const LOCAL_STORAGE_DASHBOARD_CLUSTER_SORT_KEY = 'cloudPosture:complianceDashboard:clusterSort'; -export type CloudPostureIntegrations = Record; +export type CloudPostureIntegrations = Record< + CloudSecurityPolicyTemplate, + CloudPostureIntegrationProps +>; export interface CloudPostureIntegrationProps { - policyTemplate: PosturePolicyTemplate; + policyTemplate: CloudSecurityPolicyTemplate; name: string; shortName: string; options: Array<{ @@ -50,7 +57,7 @@ export interface CloudPostureIntegrationProps { export const cloudPostureIntegrations: CloudPostureIntegrations = { cspm: { - policyTemplate: 'cspm', + policyTemplate: CSPM_POLICY_TEMPLATE, name: i18n.translate('xpack.csp.cspmIntegration.integration.nameTitle', { defaultMessage: 'Cloud Security Posture Management', }), @@ -99,7 +106,7 @@ export const cloudPostureIntegrations: CloudPostureIntegrations = { ], }, kspm: { - policyTemplate: 'kspm', + policyTemplate: KSPM_POLICY_TEMPLATE, name: i18n.translate('xpack.csp.kspmIntegration.integration.nameTitle', { defaultMessage: 'Kubernetes Security Posture Management', }), @@ -129,4 +136,17 @@ export const cloudPostureIntegrations: CloudPostureIntegrations = { }, ], }, + vuln_mgmt: { + policyTemplate: VULN_MGMT_POLICY_TEMPLATE, + name: 'Vulnerability Management', // TODO: we should use i18n and fix this + shortName: 'VULN_MGMT', // TODO: we should use i18n and fix this + options: [ + { + type: CLOUDBEAT_VULN_MGMT_AWS, + name: 'Amazon Web Services', // TODO: we should use i18n and fix this + icon: 'logoAWS', + benchmark: 'N/A', // TODO: change benchmark to be optional + }, + ], + }, }; diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/mocks.ts b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/mocks.ts index ceefc2a2b6333..219b551c14260 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/mocks.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/mocks.ts @@ -15,11 +15,15 @@ import { } from '../../../common/constants'; import type { PostureInput } from '../../../common/types'; -export const getMockPolicyAWS = () => getPolicyMock(CLOUDBEAT_AWS); -export const getMockPolicyK8s = () => getPolicyMock(CLOUDBEAT_VANILLA); -export const getMockPolicyEKS = () => getPolicyMock(CLOUDBEAT_EKS); +export const getMockPolicyAWS = () => getPolicyMock(CLOUDBEAT_AWS, 'cspm', 'aws'); +export const getMockPolicyK8s = () => getPolicyMock(CLOUDBEAT_VANILLA, 'kspm', 'self_managed'); +export const getMockPolicyEKS = () => getPolicyMock(CLOUDBEAT_EKS, 'kspm', 'eks'); -const getPolicyMock = (type: PostureInput): NewPackagePolicy => { +const getPolicyMock = ( + type: PostureInput, + posture: string, + deployment: string +): NewPackagePolicy => { const mockPackagePolicy = createNewPackagePolicyMock(); const awsVarsMock = { @@ -44,10 +48,10 @@ const getPolicyMock = (type: PostureInput): NewPackagePolicy => { }, vars: { posture: { - value: type === CLOUDBEAT_VANILLA || type === CLOUDBEAT_EKS ? 'kspm' : 'cspm', + value: posture, type: 'text', }, - deployment: { value: type, type: 'text' }, + deployment: { value: deployment, type: 'text' }, }, inputs: [ { diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx index 37d4a13e8a532..ec935f0ffc505 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx @@ -49,6 +49,23 @@ describe('', () => { onChange.mockClear(); }); + it('updates package policy namespace to default when it changes', () => { + const policy = getMockPolicyK8s(); + const { rerender } = render(); + + rerender(); + + // Listen to the 2nd triggered by the test (re-render with new policy namespace) + // The 1st is done on mount to ensure initial state is valid. + expect(onChange).toHaveBeenNthCalledWith(2, { + isValid: true, + updatedPolicy: { + ...policy, + namespace: 'default', + }, + }); + }); + it('renders and updates name field', () => { const policy = getMockPolicyK8s(); const { getByLabelText } = render(); diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx index da77b3965ed19..b0a4efa793a77 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx @@ -4,15 +4,25 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { memo, useEffect } from 'react'; +import React, { memo, useCallback, useEffect } from 'react'; import { EuiFieldText, EuiFormRow, EuiSpacer, EuiTitle } from '@elastic/eui'; import type { NewPackagePolicy } from '@kbn/fleet-plugin/public'; import { FormattedMessage } from '@kbn/i18n-react'; import type { PackagePolicyReplaceDefineStepExtensionComponentProps } from '@kbn/fleet-plugin/public/types'; import { useParams } from 'react-router-dom'; -import type { PostureInput, PosturePolicyTemplate } from '../../../common/types'; -import { CLOUDBEAT_AWS, CLOUDBEAT_VANILLA } from '../../../common/constants'; -import { getPosturePolicy, getEnabledPostureInput, getPostureInputHiddenVars } from './utils'; +import type { PostureInput, CloudSecurityPolicyTemplate } from '../../../common/types'; +import { + CLOUDBEAT_AWS, + CLOUDBEAT_VANILLA, + CLOUDBEAT_VULN_MGMT_AWS, +} from '../../../common/constants'; +import { + getPosturePolicy, + getEnabledPostureInput, + getPostureInputHiddenVars, + POSTURE_NAMESPACE, + NewPackagePolicyPostureInput, +} from './utils'; import { PolicyTemplateInfo, PolicyTemplateInputSelector, @@ -23,6 +33,7 @@ import { const DEFAULT_INPUT_TYPE = { kspm: CLOUDBEAT_VANILLA, cspm: CLOUDBEAT_AWS, + vuln_mgmt: CLOUDBEAT_VULN_MGMT_AWS, } as const; const EditScreenStepTitle = () => ( @@ -61,11 +72,13 @@ const IntegrationSettings = ({ onChange, fields }: IntegrationInfoFieldsProps) = export const CspPolicyTemplateForm = memo( ({ newPolicy, onChange, validationResults, isEditPage }) => { - const { integration } = useParams<{ integration: PosturePolicyTemplate }>(); + const { integration } = useParams<{ integration: CloudSecurityPolicyTemplate }>(); const input = getEnabledPostureInput(newPolicy); - const updatePolicy = (updatedPolicy: NewPackagePolicy) => - onChange({ isValid: true, updatedPolicy }); + const updatePolicy = useCallback( + (updatedPolicy: NewPackagePolicy) => onChange({ isValid: true, updatedPolicy }), + [onChange] + ); /** * - Updates policy inputs by user selection @@ -113,6 +126,8 @@ export const CspPolicyTemplateForm = memo {isEditPage && } @@ -159,3 +174,20 @@ CspPolicyTemplateForm.displayName = 'CspPolicyTemplateForm'; // eslint-disable-next-line import/no-default-export export { CspPolicyTemplateForm as default }; + +const useEnsureDefaultNamespace = ({ + newPolicy, + input, + updatePolicy, +}: { + newPolicy: NewPackagePolicy; + input: NewPackagePolicyPostureInput; + updatePolicy: (policy: NewPackagePolicy) => void; +}) => { + useEffect(() => { + if (newPolicy.namespace === POSTURE_NAMESPACE) return; + + const policy = { ...getPosturePolicy(newPolicy, input.type), namespace: POSTURE_NAMESPACE }; + updatePolicy(policy); + }, [newPolicy, input, updatePolicy]); +}; diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_selectors.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_selectors.tsx index 083c700c6f6e1..1a3ac98536943 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_selectors.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_selectors.tsx @@ -9,15 +9,15 @@ import { EuiSpacer, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import type { NewPackagePolicy } from '@kbn/fleet-plugin/common'; import { CSPM_POLICY_TEMPLATE, KSPM_POLICY_TEMPLATE } from '../../../common/constants'; -import type { PostureInput, PosturePolicyTemplate } from '../../../common/types'; +import type { PostureInput, CloudSecurityPolicyTemplate } from '../../../common/types'; import { getPolicyTemplateInputOptions, type NewPackagePolicyPostureInput } from './utils'; import { RadioGroup } from './csp_boxed_radio_group'; import { AwsCredentialsForm } from './aws_credentials_form'; interface PolicyTemplateSelectorProps { - selectedTemplate: PosturePolicyTemplate; + selectedTemplate: CloudSecurityPolicyTemplate; policy: NewPackagePolicy; - setPolicyTemplate(template: PosturePolicyTemplate): void; + setPolicyTemplate(template: CloudSecurityPolicyTemplate): void; disabled: boolean; } @@ -41,7 +41,7 @@ export const PolicyTemplateSelector = ({ ({ id: v, label: v.toUpperCase() }))} idSelected={selectedTemplate} - onChange={(id) => setPolicyTemplate(id as PosturePolicyTemplate)} + onChange={(id) => setPolicyTemplate(id as CloudSecurityPolicyTemplate)} disabled={disabled} />
    @@ -65,7 +65,7 @@ export const PolicyTemplateVarsForm = ({ input, ...props }: PolicyTemplateVarsFo }; interface PolicyTemplateInfoProps { - postureType: PosturePolicyTemplate; + postureType: CloudSecurityPolicyTemplate; } export const PolicyTemplateInfo = ({ postureType }: PolicyTemplateInfoProps) => ( diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.test.ts b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.test.ts index aaed63a91da0a..8e7366d9b8664 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.test.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.test.ts @@ -35,7 +35,7 @@ describe('getPosturePolicy', () => { const policy = getPosturePolicy(mockCisAws, 'cloudbeat/cis_k8s'); expect(policy.vars?.posture.value).toBe('kspm'); - expect(policy.vars?.deployment.value).toBe('cloudbeat/cis_k8s'); + expect(policy.vars?.deployment.value).toBe('self_managed'); // Does not change extra vars expect(policy.vars?.extra.value).toBe('value'); diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts index 2d8f670b5a0fd..86e0a4ec1d054 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts @@ -20,10 +20,13 @@ import { SUPPORTED_CLOUDBEAT_INPUTS, } from '../../../common/constants'; import { DEFAULT_AWS_VARS_GROUP } from './aws_credentials_form'; -import type { PostureInput, PosturePolicyTemplate } from '../../../common/types'; +import type { PostureInput, CloudSecurityPolicyTemplate } from '../../../common/types'; import { assert } from '../../../common/utils/helpers'; import { cloudPostureIntegrations } from '../../common/constants'; +// Posture policies only support the default namespace +export const POSTURE_NAMESPACE = 'default'; + type PosturePolicyInput = | { type: typeof CLOUDBEAT_AZURE; policy_template: 'cspm' } | { type: typeof CLOUDBEAT_GCP; policy_template: 'cspm' } @@ -37,11 +40,39 @@ export type NewPackagePolicyPostureInput = NewPackagePolicyInput & PosturePolicy export const isPostureInput = ( input: NewPackagePolicyInput ): input is NewPackagePolicyPostureInput => - SUPPORTED_POLICY_TEMPLATES.includes(input.policy_template as PosturePolicyTemplate) && + SUPPORTED_POLICY_TEMPLATES.includes(input.policy_template as CloudSecurityPolicyTemplate) && SUPPORTED_CLOUDBEAT_INPUTS.includes(input.type as PostureInput); -const getInputPolicyTemplate = (inputs: NewPackagePolicyInput[], inputType: PostureInput) => - inputs.filter(isPostureInput).find((i) => i.type === inputType)!.policy_template; +const getPostureType = (policyTemplateInput: PostureInput) => { + switch (policyTemplateInput) { + case CLOUDBEAT_AWS: + case CLOUDBEAT_AZURE: + case CLOUDBEAT_GCP: + return 'cspm'; + case CLOUDBEAT_VANILLA: + case CLOUDBEAT_EKS: + return 'kspm'; + default: + return 'n/a'; + } +}; + +const getDeploymentType = (policyTemplateInput: PostureInput) => { + switch (policyTemplateInput) { + case CLOUDBEAT_AWS: + return 'aws'; + case CLOUDBEAT_AZURE: + return 'azure'; + case CLOUDBEAT_GCP: + return 'gcp'; + case CLOUDBEAT_VANILLA: + return 'self_managed'; + case CLOUDBEAT_EKS: + return 'eks'; + default: + return 'n/a'; + } +}; const getPostureInput = ( input: NewPackagePolicyInput, @@ -75,12 +106,13 @@ export const getPosturePolicy = ( inputVars?: Record ): NewPackagePolicy => ({ ...newPolicy, + namespace: 'default', // Enable new policy input and disable all others inputs: newPolicy.inputs.map((item) => getPostureInput(item, inputType, inputVars)), // Set hidden policy vars vars: merge({}, newPolicy.vars, { - deployment: { value: inputType }, - posture: { value: getInputPolicyTemplate(newPolicy.inputs, inputType) }, + deployment: { value: getDeploymentType(inputType) }, + posture: { value: getPostureType(inputType) }, }), }); @@ -97,7 +129,7 @@ export const getPostureInputHiddenVars = (inputType: PostureInput) => { } }; -export const getPolicyTemplateInputOptions = (policyTemplate: PosturePolicyTemplate) => +export const getPolicyTemplateInputOptions = (policyTemplate: CloudSecurityPolicyTemplate) => cloudPostureIntegrations[policyTemplate].options.map((o) => ({ tooltip: o.tooltip, value: o.type, diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/findings_by_resource_table.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/findings_by_resource_table.tsx index f8ac3dc3ad53a..b25149208ce1a 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/findings_by_resource_table.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/findings_by_resource_table.tsx @@ -16,7 +16,7 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import numeral from '@elastic/numeral'; -import { Link, generatePath } from 'react-router-dom'; +import { generatePath, Link } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; import { ColumnNameWithTooltip } from '../../../components/column_name_with_tooltip'; import { ComplianceScoreBar } from '../../../components/compliance_score_bar'; @@ -126,7 +126,9 @@ const baseColumns: Array> = width: '15%', render: (resourceId: FindingsByResourcePage['resource_id']) => ( diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_container.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_container.tsx index 268ea6f393a99..a9598b228f3ae 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_container.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_container.tsx @@ -69,6 +69,7 @@ const getResourceFindingSharedValues = (sharedValues: { resourceSubType: string; resourceName: string; clusterId: string; + cloudAccountName: string; }): EuiDescriptionListProps['listItems'] => [ { title: i18n.translate('xpack.csp.findings.resourceFindingsSharedValues.resourceTypeTitle', { @@ -88,10 +89,18 @@ const getResourceFindingSharedValues = (sharedValues: { }), description: sharedValues.clusterId, }, + { + title: i18n.translate('xpack.csp.findings.resourceFindingsSharedValues.cloudAccountName', { + defaultMessage: 'Cloud Account Name', + }), + description: sharedValues.cloudAccountName, + }, ]; export const ResourceFindings = ({ dataView }: FindingsBaseProps) => { const params = useParams<{ resourceId: string }>(); + const decodedResourceId = decodeURIComponent(params.resourceId); + const getPersistedDefaultQuery = usePersistedQuery(getDefaultQuery); const { urlQuery, setUrlQuery } = useUrlQuery(getPersistedDefaultQuery); const { pageSize, setPageSize } = usePageSize(LOCAL_STORAGE_PAGE_SIZE_FINDINGS_KEY); @@ -111,7 +120,7 @@ export const ResourceFindings = ({ dataView }: FindingsBaseProps) => { const resourceFindings = useResourceFindings({ sort: urlQuery.sort, query: baseEsQuery.query, - resourceId: params.resourceId, + resourceId: decodedResourceId, enabled: !baseEsQuery.error, }); @@ -213,10 +222,11 @@ export const ResourceFindings = ({ dataView }: FindingsBaseProps) => { resourceFindings.data && ( ) diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/use_resource_findings.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/use_resource_findings.ts index a2314dc50b50d..17520e68bceb9 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/use_resource_findings.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/use_resource_findings.ts @@ -35,7 +35,7 @@ type ResourceFindingsResponse = IKibanaSearchResponse< >; export type ResourceFindingsResponseAggs = Record< - 'count' | 'clusterId' | 'resourceSubType' | 'resourceName', + 'count' | 'clusterId' | 'resourceSubType' | 'resourceName' | 'cloudAccountName', estypes.AggregationsMultiBucketAggregateBase< estypes.AggregationsStringRareTermsBucketKeys | undefined > @@ -59,6 +59,9 @@ const getResourceFindingsQuery = ({ sort: [{ [sort.field]: sort.direction }], aggs: { ...getFindingsCountAggQuery(), + cloudAccountName: { + terms: { field: 'cloud.account.name' }, + }, clusterId: { terms: { field: 'cluster_id' }, }, @@ -98,6 +101,7 @@ export const useResourceFindings = (options: UseResourceFindingsOptions) => { assertNonBucketsArray(aggregations.clusterId?.buckets); assertNonBucketsArray(aggregations.resourceSubType?.buckets); assertNonBucketsArray(aggregations.resourceName?.buckets); + assertNonBucketsArray(aggregations.cloudAccountName?.buckets); return { page: hits.hits.map((hit) => hit._source!), @@ -106,6 +110,7 @@ export const useResourceFindings = (options: UseResourceFindingsOptions) => { clusterId: getFirstBucketKey(aggregations.clusterId?.buckets), resourceSubType: getFirstBucketKey(aggregations.resourceSubType?.buckets), resourceName: getFirstBucketKey(aggregations.resourceName?.buckets), + cloudAccountName: getFirstBucketKey(aggregations.cloudAccountName?.buckets), }; }, onError: (err: Error) => showErrorToast(toasts, err), diff --git a/x-pack/plugins/cloud_security_posture/server/lib/fleet_util.ts b/x-pack/plugins/cloud_security_posture/server/lib/fleet_util.ts index 2e51b9bd738bc..1b66a4e401311 100644 --- a/x-pack/plugins/cloud_security_posture/server/lib/fleet_util.ts +++ b/x-pack/plugins/cloud_security_posture/server/lib/fleet_util.ts @@ -18,7 +18,7 @@ import type { PackagePolicy, } from '@kbn/fleet-plugin/common'; import { errors } from '@elastic/elasticsearch'; -import { PosturePolicyTemplate } from '../../common/types'; +import type { CloudSecurityPolicyTemplate } from '../../common/types'; import { SUPPORTED_POLICY_TEMPLATES } from '../../common/constants'; import { CSP_FLEET_PACKAGE_KUERY } from '../../common/utils/helpers'; import { @@ -31,7 +31,7 @@ export const PACKAGE_POLICY_SAVED_OBJECT_TYPE = 'ingest-package-policies'; const isFleetMissingAgentHttpError = (error: unknown) => error instanceof errors.ResponseError && error.statusCode === 404; -const isPolicyTemplate = (input: any): input is PosturePolicyTemplate => +const isPolicyTemplate = (input: any): input is CloudSecurityPolicyTemplate => SUPPORTED_POLICY_TEMPLATES.includes(input); const getPackageNameQuery = (packageName: string, benchmarkFilter?: string): string => { diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/file_based_expanded_row.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/file_based_expanded_row.tsx index ebe37dda153f4..89504a04ec342 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/file_based_expanded_row.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/file_based_expanded_row.tsx @@ -6,6 +6,7 @@ */ import React from 'react'; +import { useExpandedRowCss } from './use_expanded_row_css'; import { BooleanContent, DateContent, @@ -22,6 +23,7 @@ import type { FileBasedFieldVisConfig } from '../../../../../common/types/field_ export const FileBasedDataVisualizerExpandedRow = ({ item }: { item: FileBasedFieldVisConfig }) => { const config = item; const { type, fieldName } = config; + const dvExpandedRow = useExpandedRowCss(); function getCardContent() { switch (type) { @@ -53,7 +55,7 @@ export const FileBasedDataVisualizerExpandedRow = ({ item }: { item: FileBasedFi } return ( -
    +
    {getCardContent()}
    ); diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/index_based_expanded_row.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/index_based_expanded_row.tsx index afaf3475f8691..e164a37307daf 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/index_based_expanded_row.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/index_based_expanded_row.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { DataView, DataViewField } from '@kbn/data-views-plugin/public'; +import { useExpandedRowCss } from './use_expanded_row_css'; import { GeoPointContentWithMap } from './geo_point_content_with_map'; import { SUPPORTED_FIELD_TYPES } from '../../../../../common/constants'; import { @@ -42,6 +43,7 @@ export const IndexBasedDataVisualizerExpandedRow = ({ }) => { const config = { ...item, stats: { ...item.stats, totalDocuments } }; const { loading, type, existsInDocs, fieldName } = config; + const dvExpandedRow = useExpandedRowCss(); function getCardContent() { if (existsInDocs === false) { @@ -88,7 +90,7 @@ export const IndexBasedDataVisualizerExpandedRow = ({ } return ( -
    +
    {loading === true ? : getCardContent()}
    ); diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/use_expanded_row_css.ts b/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/use_expanded_row_css.ts new file mode 100644 index 0000000000000..3d111923b8b7a --- /dev/null +++ b/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/use_expanded_row_css.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 { useEuiTheme } from '@elastic/eui'; +import { css } from '@emotion/react'; + +export const useExpandedRowCss = () => { + const { euiTheme } = useEuiTheme(); + + return css({ + width: '100%', + paddingLeft: `calc(${euiTheme.size.base} * 3)`, + }); +}; diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/field_count_panel/field_count_panel.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/field_count_panel/field_count_panel.tsx index c5343e4a304c3..d3f3cd4dd8e18 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/field_count_panel/field_count_panel.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/field_count_panel/field_count_panel.tsx @@ -5,37 +5,51 @@ * 2.0. */ -import { EuiFlexGroup, EuiFlexItem, EuiSwitch } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiSwitch, useEuiTheme } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import React, { FC } from 'react'; -import type { - MetricFieldsCountProps, - TotalFieldsCountProps, +import { css } from '@emotion/react'; +import { useEuiBreakpoint } from '@elastic/eui'; +import { + type MetricFieldsCountProps, + type TotalFieldsCountProps, + MetricFieldsCount, + TotalFieldsCount, + dvFieldCountItemCss, } from '../stats_table/components/field_count_stats'; -import { MetricFieldsCount, TotalFieldsCount } from '../stats_table/components/field_count_stats'; interface Props extends TotalFieldsCountProps, MetricFieldsCountProps { showEmptyFields: boolean; toggleShowEmptyFields: () => void; } + export const FieldCountPanel: FC = ({ metricsStats, fieldsCountStats, showEmptyFields, toggleShowEmptyFields, }) => { + const { euiTheme } = useEuiTheme(); + + const dvFieldCountPanelCss = css({ + marginLeft: euiTheme.size.xs, + [useEuiBreakpoint(['xs', 's'])]: { + flexDirection: 'column', + alignItems: 'flex-start', + }, + }); return ( - + = ({ {Array.isArray(asyncHrefCards) && asyncHrefCards.map((link) => ( - + } data-test-subj="fileDataVisLink" diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/_index.scss b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/_index.scss index 5aaf24b82a1a6..ccd38b8506a93 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/_index.scss +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/_index.scss @@ -1,93 +1,2 @@ @import 'components/field_data_expanded_row/index'; -@import 'components/field_count_stats/index'; @import 'components/field_data_row/index'; - -$panelWidthS: #{'max(20%, 225px)'}; -$panelWidthM: #{'max(30%, 300px)'}; -$panelWidthL: #{'max(40%, 450px)'}; - -.dvExpandedRow { - padding-left: $euiSize * 4; - width: 100%; - - .dvExpandedRow__fieldHeader { - text-transform: uppercase; - text-align: left; - color: $euiColorDarkShade; - font-weight: bold; - padding-bottom: $euiSizeS; - } -} - -@include euiBreakpoint('m', 'l', 'xl') { - .dvTable { - .columnHeader__title { - display: flex; - align-items: center; - } - - .columnHeader__icon { - padding-right: $euiSizeXS; - } - - .euiTableRow > .euiTableRowCell { - border-top: 0; - border-bottom: $euiBorderThin; - } - - .euiTableCellContent { - padding: $euiSizeXS; - } - - .euiTableRow-isExpandedRow { - - .euiTableRowCell { - background-color: $euiColorEmptyShade !important; - border-top: 0; - border-bottom: $euiBorderThin; - &:hover { - background-color: $euiColorEmptyShade !important; - } - } - } - - .dvSummaryTable { - .euiTableHeaderCell { - display: none; - } - } - - .dvSummaryTable__wrapper { - min-width: $panelWidthS; - max-width: $panelWidthS; - - &.dvPanel__dateSummary { - min-width: $panelWidthM; - max-width: $panelWidthM; - } - } - - .dvTopValues__wrapper { - min-width: fit-content; - } - - .dvPanel__wrapper { - margin: $euiSizeXS $euiSizeM $euiSizeM 0; - &.dvPanel--compressed { - width: $panelWidthS; - } - &.dvPanel--uniform { - min-width: $panelWidthS; - max-width: $panelWidthS; - } - } - - .dvMap__wrapper { - height: $euiSize * 15; //240px - } - - .dvText__wrapper { - min-width: $panelWidthS; - } - } -} diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/expanded_row_field_header/expanded_row_field_header.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/expanded_row_field_header/expanded_row_field_header.tsx index 8fdb68c6efa4c..f0bd29c056985 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/expanded_row_field_header/expanded_row_field_header.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/expanded_row_field_header/expanded_row_field_header.tsx @@ -5,16 +5,24 @@ * 2.0. */ -import { EuiText } from '@elastic/eui'; +import { EuiText, useEuiTheme } from '@elastic/eui'; import React from 'react'; +import { css } from '@emotion/react'; -export const ExpandedRowFieldHeader = ({ children }: { children: React.ReactNode }) => ( - - {children} - -); +export const ExpandedRowFieldHeader = ({ children }: { children: React.ReactNode }) => { + const { euiTheme } = useEuiTheme(); + + const dvExpandedRowFieldHeader = css({ + textTransform: 'uppercase', + textAlign: 'left', + color: euiTheme.colors.darkShade, + fontWeight: 'bold', + paddingBottom: euiTheme.size.s, + }); + + return ( + + {children} + + ); +}; diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_count_stats/_index.scss b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_count_stats/_index.scss deleted file mode 100644 index f3c6eed07781b..0000000000000 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_count_stats/_index.scss +++ /dev/null @@ -1,12 +0,0 @@ -.dvFieldCount__panel { - margin-left: $euiSizeXS; - @include euiBreakpoint('xs', 's') { - flex-direction: column; - align-items: flex-start; - } -} - -.dvFieldCount__item { - max-width: 300px; - min-width: 200px; -} diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_count_stats/index.ts b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_count_stats/index.ts index ccd7c6b73a577..3027621254028 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_count_stats/index.ts +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_count_stats/index.ts @@ -9,3 +9,4 @@ export type { TotalFieldsCountProps, TotalFieldsStats } from './total_fields_cou export { TotalFieldsCount } from './total_fields_count'; export type { MetricFieldsCountProps, MetricFieldsStats } from './metric_fields_count'; export { MetricFieldsCount } from './metric_fields_count'; +export { dvFieldCountItemCss } from './styles'; diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_count_stats/metric_fields_count.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_count_stats/metric_fields_count.tsx index ebfba7d79b25a..8d4290654ebbc 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_count_stats/metric_fields_count.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_count_stats/metric_fields_count.tsx @@ -8,6 +8,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiNotificationBadge, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import React, { FC } from 'react'; +import { dvFieldCountItemCss } from './styles'; export interface MetricFieldsStats { visibleMetricsCount: number; @@ -28,10 +29,10 @@ export const MetricFieldsCount: FC = ({ metricsStats }) <> {metricsStats && ( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/index.ts b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_count_stats/styles.ts similarity index 57% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/index.ts rename to x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_count_stats/styles.ts index 8dde6bf2df877..d97c7f53bc519 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/index.ts +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_count_stats/styles.ts @@ -5,7 +5,9 @@ * 2.0. */ -export * from './bulk_create_threshold_signals'; -export * from './get_threshold_bucket_filters'; -export * from './get_threshold_signal_history'; -export * from './find_threshold_signals'; +import { css } from '@emotion/react'; + +export const dvFieldCountItemCss = css({ + maxWidth: '300px', + minWidth: '200px', +}); diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_count_stats/total_fields_count.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_count_stats/total_fields_count.tsx index ac91da964dd9b..f4223610a0914 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_count_stats/total_fields_count.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_count_stats/total_fields_count.tsx @@ -8,6 +8,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiNotificationBadge, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import React, { FC } from 'react'; +import { dvFieldCountItemCss } from './styles'; export interface TotalFieldsStats { visibleFieldsCount: number; @@ -28,10 +29,10 @@ export const TotalFieldsCount: FC = ({ fieldsCountStats } return ( diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/_index.scss b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/_index.scss index b878bf0dcc0f6..fdc591a140fea 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/_index.scss +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/_index.scss @@ -1,7 +1 @@ @import 'number_content'; - -.dataVisualizerExpandedRow { - @include euiBreakpoint('xs', 's', 'm') { - flex-direction: column; - } -} diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/expanded_row_content.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/expanded_row_content.tsx index f638ac8002810..3f4dfc7e3fe4c 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/expanded_row_content.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/expanded_row_content.tsx @@ -14,7 +14,7 @@ interface Props { } export const ExpandedRowContent: FC = ({ children, dataTestSubj }) => { return ( - + {children} ); diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/data_visualizer_stats_table.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/data_visualizer_stats_table.tsx index 7e13e981781dd..51c31dd3af21c 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/data_visualizer_stats_table.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/data_visualizer_stats_table.tsx @@ -21,6 +21,7 @@ import { EuiResizeObserver, EuiLoadingSpinner, useEuiTheme, + useEuiMinBreakpoint, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { EuiTableComputedColumnType } from '@elastic/eui/src/components/basic_table/table_types'; @@ -383,6 +384,85 @@ export const DataVisualizerTable = ({ return getItemIdToExpandedRowMap(itemIds, items); }, [items, expandedRowItemIds, getItemIdToExpandedRowMap]); + const $panelWidthS = `calc(max(20%, 225px))`; + const $panelWidthM = `calc(max(30%, 300px))`; + + const dvTableCss = css({ + thead: { + position: 'sticky', + insetBlockStart: 0, + zIndex: 1, + backgroundColor: euiTheme.colors.emptyShade, + boxShadow: `inset 0 0px 0, inset 0 -1px 0 ${euiTheme.border.color}`, + }, + '.euiTableRow > .euiTableRowCel': { + 'border-top': 0, + }, + [useEuiMinBreakpoint('s')]: { + '& .columnHeader__title': { + display: 'flex', + alignItems: 'center', + }, + '& .columnHeader__icon': { + paddingRight: euiTheme.size.xs, + }, + '& .euiTableRow > .euiTableRowCell': { + borderTop: 0, + borderBottom: euiTheme.border.thin, + }, + '& .euiTableCellContent': { + padding: euiTheme.size.xs, + }, + '& .euiTableRow-isExpandedRow': { + '.euiTableRowCell': { + backgroundColor: `${euiTheme.colors.emptyShade} !important`, + borderTop: 0, + borderBottom: euiTheme.border.thin, + '&:hover': { + backgroundColor: `${euiTheme.colors.emptyShade} !important`, + }, + }, + }, + '& .dvSummaryTable': { + '.euiTableHeaderCell': { + display: 'none', + }, + }, + '& .dvSummaryTable__wrapper': { + minWidth: $panelWidthS, + maxWidth: $panelWidthS, + '&.dvPanel__dateSummary': { + minWidth: $panelWidthM, + maxWidth: $panelWidthM, + }, + }, + '& .dvTopValues__wrapper': { + minWidth: 'fit-content', + }, + '& .dvPanel__wrapper': { + '&.dvPanel--compressed': { + width: $panelWidthS, + }, + '&.dvPanel--uniform': { + minWidth: $panelWidthS, + maxWidth: $panelWidthS, + }, + }, + '& .dvPanel__wrapper:not(:last-child)': { + margin: `${euiTheme.size.xs} ${euiTheme.size.m} ${euiTheme.size.m} 0`, + }, + '& .dvPanel__wrapper:last-child': { + margin: `${euiTheme.size.xs} 0 ${euiTheme.size.m} 0`, + }, + + '& .dvMap__wrapper': { + height: '240px', + }, + '& .dvText__wrapper': { + minWidth: $panelWidthS, + }, + }, + }); return ( {(resizeRef) => ( @@ -395,7 +475,7 @@ export const DataVisualizerTable = ({ }) : undefined } - className={'dvTable'} + css={dvTableCss} items={items} itemId={FIELD_NAME} columns={columns} @@ -409,18 +489,6 @@ export const DataVisualizerTable = ({ rowProps={(item) => ({ 'data-test-subj': `dataVisualizerRow row-${item.fieldName}`, })} - css={css` - thead { - position: sticky; - inset-block-start: 0; - z-index: 1; - background-color: ${euiTheme.colors.emptyShade}; - box-shadow: inset 0 0px 0, inset 0 -1px 0 ${euiTheme.border.color}; - } - .euiTableRow > .euiTableRowCel { - border-top: 0px; - } - `} />
    )} diff --git a/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/import_errors/errors.tsx b/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/import_errors/errors.tsx index 957c84acc6cf8..381264a6483ba 100644 --- a/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/import_errors/errors.tsx +++ b/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/import_errors/errors.tsx @@ -128,17 +128,19 @@ function toString(error: any): ImportError { return { msg: error.msg }; } else if (error.error !== undefined) { if (typeof error.error === 'object') { - if (error.error.reason !== undefined) { - // this will catch a bulk ingest failure - const errorObj: ImportError = { msg: error.error.reason }; - if (error.error.root_cause !== undefined) { - errorObj.more = JSON.stringify(error.error.root_cause); + // this will catch a bulk ingest failure + const reason = error.error.reason ?? error.error.meta.body.error.reason; + if (reason !== undefined) { + const errorObj: ImportError = { msg: reason }; + const rootCause = error.error.root_cause ?? error.error.meta.body.error.root_cause; + if (rootCause !== undefined) { + errorObj.more = JSON.stringify(rootCause); } return errorObj; } + // this will catch javascript errors such as JSON parsing issues if (error.error.message !== undefined) { - // this will catch javascript errors such as JSON parsing issues return { msg: error.error.message }; } } else { diff --git a/x-pack/plugins/enterprise_search/common/connectors/native_connectors.ts b/x-pack/plugins/enterprise_search/common/connectors/native_connectors.ts index ad35e96b32f77..c6c2500f78857 100644 --- a/x-pack/plugins/enterprise_search/common/connectors/native_connectors.ts +++ b/x-pack/plugins/enterprise_search/common/connectors/native_connectors.ts @@ -130,14 +130,24 @@ export const NATIVE_CONNECTOR_DEFINITIONS: Record { - + + + + + + + + + diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_search_preview/search_ui_components.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_search_preview/search_ui_components.tsx index cf468953f415c..c685cf5898e3d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_search_preview/search_ui_components.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_search_preview/search_ui_components.tsx @@ -19,16 +19,20 @@ import { EuiFlexItem, EuiIcon, EuiPanel, + EuiSelect, EuiText, EuiTextColor, + EuiTitle, } from '@elastic/eui'; import type { InputViewProps, + PagingInfoViewProps, ResultViewProps, + ResultsPerPageViewProps, ResultsViewProps, } from '@elastic/react-search-ui-views'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; +import { FormattedMessage, FormattedHTMLMessage } from '@kbn/i18n-react'; import { indexHealthToHealthColor } from '../../../../shared/constants/health_colors'; @@ -171,3 +175,52 @@ export const InputView: React.FC = ({ getInputProps }) => { ); }; + +export const PagingInfoView: React.FC = ({ start, end, totalResults }) => ( + + + +); + +export const RESULTS_PER_PAGE_OPTIONS = [10, 20, 50]; + +export const ResultsPerPageView: React.FC = ({ + onChange, + options, + value, +}) => ( + + + + + + ({ + text: i18n.translate( + 'xpack.enterpriseSearch.content.engine.searchPreview.resultsPerPage.option.label', + { + defaultMessage: '{value} {value, plural, one {Result} other {Results}}', + values: { value: option }, + } + ), + value: option, + })) ?? [] + } + value={value} + onChange={(evt) => onChange(parseInt(evt.target.value, 10))} + /> + + +); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/constants.ts b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/constants.ts index 35e1942bdc3de..fb490ce35b7fe 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/constants.ts @@ -11,7 +11,7 @@ export const FLASH_MESSAGE_TYPES = { success: { color: 'success' as FlashMessageColors, iconType: 'check' }, info: { color: 'primary' as FlashMessageColors, iconType: 'iInCircle' }, warning: { color: 'warning' as FlashMessageColors, iconType: 'alert' }, - error: { color: 'danger' as FlashMessageColors, iconType: 'alert' }, + error: { color: 'danger' as FlashMessageColors, iconType: 'error' }, }; // This is the default amount of time (5 seconds) a toast will last before disappearing diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/flash_messages.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/flash_messages.test.tsx index 757e5509773ac..dd1aef467d3dc 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/flash_messages.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/flash_messages.test.tsx @@ -69,7 +69,7 @@ describe('Toasts', () => { }, { color: 'danger', - iconType: 'alert', + iconType: 'error', title: 'Oh no!', text:
    Something went wrong
    , id: 'errorToastId', diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/set_message_helpers.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/set_message_helpers.test.ts index 6d56c7b202797..25da2c2f728a2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/set_message_helpers.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/set_message_helpers.test.ts @@ -104,7 +104,7 @@ describe('Flash Message Helpers', () => { expect(FlashMessagesLogic.values.toastMessages).toEqual([ { color: 'danger', - iconType: 'alert', + iconType: 'error', title: 'Something went wrong', id: 'errorToast-1234567890', }, @@ -142,7 +142,7 @@ describe('Flash Message Helpers', () => { expect(FlashMessagesLogic.values.toastMessages).toEqual([ { color: 'danger', - iconType: 'alert', + iconType: 'error', title: 'Something went wrong', text: "Here's some helpful advice on what to do", toastLifeTimeMs: 50000, diff --git a/x-pack/plugins/enterprise_search/server/lib/engines/field_capabilities.test.ts b/x-pack/plugins/enterprise_search/server/lib/engines/field_capabilities.test.ts new file mode 100644 index 0000000000000..3e250e4ec9109 --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/lib/engines/field_capabilities.test.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FieldCapsResponse } from '@elastic/elasticsearch/lib/api/types'; +import { IScopedClusterClient } from '@kbn/core-elasticsearch-server'; + +import { EnterpriseSearchEngineDetails } from '../../../common/types/engines'; + +import { fetchEngineFieldCapabilities } from './field_capabilities'; + +describe('engines field_capabilities', () => { + const mockClient = { + asCurrentUser: { + fieldCaps: jest.fn(), + }, + asInternalUser: {}, + }; + const mockEngine: EnterpriseSearchEngineDetails = { + created: '1999-12-31T23:59:59.999Z', + indices: [], + name: 'unit-test-engine', + updated: '1999-12-31T23:59:59.999Z', + }; + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('gets engine alias field capabilities', async () => { + const fieldCapsResponse = {} as FieldCapsResponse; + + mockClient.asCurrentUser.fieldCaps.mockResolvedValueOnce(fieldCapsResponse); + await expect( + fetchEngineFieldCapabilities(mockClient as unknown as IScopedClusterClient, mockEngine) + ).resolves.toEqual({ + created: mockEngine.created, + field_capabilities: fieldCapsResponse, + name: mockEngine.name, + updated: mockEngine.updated, + }); + + expect(mockClient.asCurrentUser.fieldCaps).toHaveBeenCalledTimes(1); + expect(mockClient.asCurrentUser.fieldCaps).toHaveBeenCalledWith({ + fields: '*', + include_unmapped: true, + index: 'search-engine-unit-test-engine', + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/server/lib/engines/field_capabilities.ts b/x-pack/plugins/enterprise_search/server/lib/engines/field_capabilities.ts new file mode 100644 index 0000000000000..ed42ab744621c --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/lib/engines/field_capabilities.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 { IScopedClusterClient } from '@kbn/core-elasticsearch-server'; + +import { + EnterpriseSearchEngineDetails, + EnterpriseSearchEngineFieldCapabilities, +} from '../../../common/types/engines'; + +export const fetchEngineFieldCapabilities = async ( + client: IScopedClusterClient, + engine: EnterpriseSearchEngineDetails +): Promise => { + const { created, name, updated } = engine; + const fieldCapabilities = await client.asCurrentUser.fieldCaps({ + fields: '*', + include_unmapped: true, + index: getEngineIndexAliasName(name), + }); + return { + created, + field_capabilities: fieldCapabilities, + name, + updated, + }; +}; + +// Note: This will likely need to be modified when engines move to es module +const getEngineIndexAliasName = (engineName: string): string => `search-engine-${engineName}`; diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/engines.test.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/engines.test.ts index 83e31f1ddf03d..019cca3a7acc2 100644 --- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/engines.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/engines.test.ts @@ -7,6 +7,19 @@ import { mockDependencies, mockRequestHandler, MockRouter } from '../../__mocks__'; +jest.mock('../../utils/fetch_enterprise_search', () => ({ + ...jest.requireActual('../../utils/fetch_enterprise_search'), + fetchEnterpriseSearch: jest.fn(), +})); +jest.mock('../../lib/engines/field_capabilities', () => ({ + fetchEngineFieldCapabilities: jest.fn(), +})); + +import { RequestHandlerContext } from '@kbn/core/server'; + +import { fetchEngineFieldCapabilities } from '../../lib/engines/field_capabilities'; +import { fetchEnterpriseSearch } from '../../utils/fetch_enterprise_search'; + import { registerEnginesRoutes } from './engines'; describe('engines routes', () => { @@ -298,4 +311,118 @@ describe('engines routes', () => { mockRouter.shouldThrow(request); }); }); + + describe('GET /internal/enterprise_search/engines/{engine_name}/field_capabilities', () => { + let mockRouter: MockRouter; + const mockClient = { + asCurrentUser: {}, + }; + const mockCore = { + elasticsearch: { client: mockClient }, + savedObjects: { client: {} }, + }; + + beforeEach(() => { + jest.clearAllMocks(); + + const context = { + core: Promise.resolve(mockCore), + } as unknown as jest.Mocked; + + mockRouter = new MockRouter({ + context, + method: 'get', + path: '/internal/enterprise_search/engines/{engine_name}/field_capabilities', + }); + + registerEnginesRoutes({ + ...mockDependencies, + router: mockRouter.router, + }); + }); + + it('fetches engine fields', async () => { + const engineResult = { + created: '1999-12-31T23:59:59.999Z', + indices: [], + name: 'unit-test', + updated: '1999-12-31T23:59:59.999Z', + }; + const fieldCapabilitiesResult = { + name: 'unit-test', + }; + + (fetchEnterpriseSearch as jest.Mock).mockResolvedValueOnce(engineResult); + (fetchEngineFieldCapabilities as jest.Mock).mockResolvedValueOnce(fieldCapabilitiesResult); + + await mockRouter.callRoute({ + params: { engine_name: 'unit-test' }, + }); + + expect(fetchEnterpriseSearch).toHaveBeenCalledWith( + expect.anything(), + expect.anything(), + '/api/engines/unit-test' + ); + expect(fetchEngineFieldCapabilities).toHaveBeenCalledWith(mockClient, engineResult); + expect(mockRouter.response.ok).toHaveBeenCalledWith({ + body: fieldCapabilitiesResult, + headers: { 'content-type': 'application/json' }, + }); + }); + it('returns 404 when fetch engine is undefined', async () => { + (fetchEnterpriseSearch as jest.Mock).mockResolvedValueOnce(undefined); + await mockRouter.callRoute({ + params: { engine_name: 'unit-test' }, + }); + + expect(mockRouter.response.customError).toHaveBeenCalledWith({ + body: { + attributes: { + error_code: 'engine_not_found', + }, + message: 'Could not find engine', + }, + statusCode: 404, + }); + }); + it('returns 404 when fetch engine is returns 404', async () => { + (fetchEnterpriseSearch as jest.Mock).mockResolvedValueOnce({ + responseStatus: 404, + responseStatusText: 'NOT_FOUND', + }); + await mockRouter.callRoute({ + params: { engine_name: 'unit-test' }, + }); + + expect(mockRouter.response.customError).toHaveBeenCalledWith({ + body: { + attributes: { + error_code: 'engine_not_found', + }, + message: 'Could not find engine', + }, + statusCode: 404, + }); + }); + it('returns error when fetch engine returns an error', async () => { + (fetchEnterpriseSearch as jest.Mock).mockResolvedValueOnce({ + responseStatus: 500, + responseStatusText: 'INTERNAL_SERVER_ERROR', + }); + await mockRouter.callRoute({ + params: { engine_name: 'unit-test' }, + }); + + expect(mockRouter.response.customError).toHaveBeenCalledWith({ + body: { + attributes: { + error_code: 'uncaught_exception', + }, + message: 'Error fetching engine', + }, + statusCode: 500, + }); + }); + }); }); diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/engines.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/engines.ts index 71b0061a7d1ba..111c04e6cd42e 100644 --- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/engines.ts +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/engines.ts @@ -6,15 +6,22 @@ */ import { schema } from '@kbn/config-schema'; +import { EnterpriseSearchEngineDetails } from '../../../common/types/engines'; +import { ErrorCode } from '../../../common/types/error_codes'; import { createApiKey } from '../../lib/engines/create_api_key'; +import { fetchEngineFieldCapabilities } from '../../lib/engines/field_capabilities'; import { RouteDependencies } from '../../plugin'; + +import { createError } from '../../utils/create_error'; import { elasticsearchErrorHandler } from '../../utils/elasticsearch_error_handler'; +import { fetchEnterpriseSearch, isResponseError } from '../../utils/fetch_enterprise_search'; export function registerEnginesRoutes({ - router, + config, enterpriseSearchRequestHandler, log, + router, }: RouteDependencies) { router.get( { @@ -134,8 +141,37 @@ export function registerEnginesRoutes({ path: '/internal/enterprise_search/engines/{engine_name}/field_capabilities', validate: { params: schema.object({ engine_name: schema.string() }) }, }, - enterpriseSearchRequestHandler.createRequest({ - path: '/api/engines/:engine_name/field_capabilities', + elasticsearchErrorHandler(log, async (context, request, response) => { + const engineName = decodeURIComponent(request.params.engine_name); + const { client } = (await context.core).elasticsearch; + + const engine = await fetchEnterpriseSearch( + config, + request, + `/api/engines/${engineName}` + ); + if (!engine || (isResponseError(engine) && engine.responseStatus === 404)) { + return createError({ + errorCode: ErrorCode.ENGINE_NOT_FOUND, + message: 'Could not find engine', + response, + statusCode: 404, + }); + } + if (isResponseError(engine)) { + return createError({ + errorCode: ErrorCode.UNCAUGHT_EXCEPTION, + message: 'Error fetching engine', + response, + statusCode: engine.responseStatus, + }); + } + + const data = await fetchEngineFieldCapabilities(client, engine); + return response.ok({ + body: data, + headers: { 'content-type': 'application/json' }, + }); }) ); } diff --git a/x-pack/plugins/enterprise_search/server/utils/fetch_enterprise_search.test.ts b/x-pack/plugins/enterprise_search/server/utils/fetch_enterprise_search.test.ts new file mode 100644 index 0000000000000..20fe7b57350ee --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/utils/fetch_enterprise_search.test.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 '../__mocks__/http_agent.mock'; + +jest.mock('node-fetch'); +import fetch from 'node-fetch'; + +import { KibanaRequest } from '@kbn/core/server'; + +import { ConfigType } from '..'; + +import { fetchEnterpriseSearch, isResponseError } from './fetch_enterprise_search'; + +describe('fetchEnterpriseSearch', () => { + const mockConfig = { + accessCheckTimeout: 200, + accessCheckTimeoutWarning: 100, + host: 'http://localhost:3002', + }; + const mockRequest = { + headers: { authorization: '==someAuth' }, + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('returns json fetch response', async () => { + const response = { foo: 'bar' }; + (fetch as unknown as jest.Mock).mockResolvedValueOnce({ + json: jest.fn().mockResolvedValueOnce(response), + ok: true, + }); + await expect( + fetchEnterpriseSearch(mockConfig as ConfigType, mockRequest as KibanaRequest, '/api/v1/test') + ).resolves.toBe(response); + }); + it('calls expected endpoint', async () => { + (fetch as unknown as jest.Mock).mockResolvedValueOnce({ + json: jest.fn().mockResolvedValueOnce({}), + ok: true, + }); + await fetchEnterpriseSearch( + mockConfig as ConfigType, + mockRequest as KibanaRequest, + '/api/v1/test' + ); + + expect(fetch).toHaveBeenCalledTimes(1); + expect(fetch).toHaveBeenCalledWith('http://localhost:3002/api/v1/test', expect.anything()); + }); + it('uses request auth header & config custom headers', async () => { + (fetch as unknown as jest.Mock).mockResolvedValueOnce({ + json: jest.fn().mockResolvedValueOnce({}), + ok: true, + }); + const config = { + ...mockConfig, + customHeaders: { + foo: 'bar', + }, + }; + await fetchEnterpriseSearch( + config as unknown as ConfigType, + mockRequest as KibanaRequest, + '/api/v1/test' + ); + + expect(fetch).toHaveBeenCalledTimes(1); + expect(fetch).toHaveBeenCalledWith(expect.anything(), { + agent: expect.anything(), + headers: { + Authorization: mockRequest.headers.authorization, + foo: 'bar', + }, + }); + }); + it('returns undefined when config.host is unavailable', async () => { + await expect( + fetchEnterpriseSearch( + { host: '' } as ConfigType, + mockRequest as KibanaRequest, + '/api/v1/test' + ) + ).resolves.toBeUndefined(); + }); +}); + +describe('isResponseError', () => { + it('returns true for ResponseError object', () => { + expect(isResponseError({ responseStatus: 404, responseStatusText: 'NOT_FOUND' })).toBe(true); + }); + it('returns false for null/undefined', () => { + expect(isResponseError(null)).toBe(false); + expect(isResponseError(undefined)).toBe(false); + }); + it('returns false for object without expected keys', () => { + expect(isResponseError({})).toBe(false); + expect(isResponseError({ responseStatusText: 'NOT_FOUND' })).toBe(false); + expect(isResponseError({ responseStatus: 404 })).toBe(false); + expect(isResponseError([])).toBe(false); + }); + it('returns false for non-object', () => { + expect(isResponseError(100)).toBe(false); + expect(isResponseError('test')).toBe(false); + }); +}); diff --git a/x-pack/plugins/enterprise_search/server/utils/fetch_enterprise_search.ts b/x-pack/plugins/enterprise_search/server/utils/fetch_enterprise_search.ts new file mode 100644 index 0000000000000..b1a2146a86ab9 --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/utils/fetch_enterprise_search.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import fetch, { RequestInit } from 'node-fetch'; + +import { KibanaRequest } from '@kbn/core/server'; + +import { ConfigType } from '..'; + +import { entSearchHttpAgent } from '../lib/enterprise_search_http_agent'; + +export interface ResponseError { + responseStatus: number; + responseStatusText: string; +} + +export function isResponseError(resp: unknown): resp is ResponseError { + if (typeof resp !== 'object') return false; + if (resp === null) return false; + if ('responseStatus' in resp && 'responseStatusText' in resp) return true; + return false; +} + +export async function fetchEnterpriseSearch( + config: ConfigType, + request: KibanaRequest, + endpoint: string +): Promise { + if (!config.host) return undefined; + + const enterpriseSearchUrl = encodeURI(`${config.host}${endpoint}`); + const options: RequestInit = { + agent: entSearchHttpAgent.getHttpAgent(), + headers: { + Authorization: request.headers.authorization as string, + ...config.customHeaders, + }, + }; + + const response = await fetch(enterpriseSearchUrl, options); + + if (!response.ok) { + return { + responseStatus: response.status, + responseStatusText: response.statusText, + }; + } + + return await response.json(); +} diff --git a/x-pack/plugins/fleet/common/constants/epm.ts b/x-pack/plugins/fleet/common/constants/epm.ts index c95754bc265d7..f380d66d0223c 100644 --- a/x-pack/plugins/fleet/common/constants/epm.ts +++ b/x-pack/plugins/fleet/common/constants/epm.ts @@ -24,6 +24,8 @@ export const USER_SETTINGS_TEMPLATE_SUFFIX = '@custom'; export const FLEET_ELASTIC_AGENT_DETAILS_DASHBOARD_ID = 'elastic_agent-f47f18cc-9c7d-4278-b2ea-a6dee816d395'; + +export const DATASET_VAR_NAME = 'data_stream.dataset'; /* Package rules: | | autoUpdatePackages | diff --git a/x-pack/plugins/fleet/common/constants/routes.ts b/x-pack/plugins/fleet/common/constants/routes.ts index 527eba8cfdb42..7eb00dd89ab0c 100644 --- a/x-pack/plugins/fleet/common/constants/routes.ts +++ b/x-pack/plugins/fleet/common/constants/routes.ts @@ -34,6 +34,7 @@ export const EPM_API_ROUTES = { DELETE_PATTERN: EPM_PACKAGES_ONE, FILEPATH_PATTERN: `${EPM_PACKAGES_ONE}/{filePath*}`, CATEGORIES_PATTERN: `${EPM_API_ROOT}/categories`, + VERIFICATION_KEY_ID: `${EPM_API_ROOT}/verification_key_id`, STATS_PATTERN: `${EPM_PACKAGES_MANY}/{pkgName}/stats`, INFO_PATTERN_DEPRECATED: EPM_PACKAGES_ONE_DEPRECATED, diff --git a/x-pack/plugins/fleet/common/openapi/bundled.json b/x-pack/plugins/fleet/common/openapi/bundled.json index 2f4ce3c78f114..eb7dcf0cad55c 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.json +++ b/x-pack/plugins/fleet/common/openapi/bundled.json @@ -252,6 +252,72 @@ ] } }, + "/epm/verification_key_id": { + "get": { + "summary": "Get package signature verification key ID", + "tags": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "body": { + "type": "object", + "properties": { + "id": { + "type": "string", + "nullable": true, + "description": "the key ID of the GPG key used to verify package signatures" + } + } + }, + "statusCode": { + "type": "number" + }, + "headers": { + "type": "object" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + } + }, + "operationId": "packages-get-verification-key-id" + }, + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "pkgName", + "in": "path", + "required": true + }, + { + "schema": { + "type": "string" + }, + "name": "pkgVersion", + "in": "path", + "required": true + }, + { + "schema": { + "type": "string" + }, + "name": "filePath", + "in": "path", + "required": true + } + ] + }, "/epm/categories": { "get": { "summary": "Package categories", @@ -2012,7 +2078,7 @@ "required": true } ], - "put": { + "post": { "summary": "Agent - Reassign", "tags": [], "responses": { @@ -2054,6 +2120,50 @@ } } } + }, + "put": { + "summary": "Agent - Reassign", + "tags": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + } + }, + "operationId": "reassign-agent-deprecated", + "parameters": [ + { + "$ref": "#/components/parameters/kbn_xsrf" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "policy_id": { + "type": "string" + } + }, + "required": [ + "policy_id" + ] + } + } + } + }, + "deprecated": true } }, "/agents/{agentId}/unenroll": { @@ -2689,7 +2799,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/new_agent_policy" + "$ref": "#/components/schemas/agent_policy_create_request" } } } @@ -2774,7 +2884,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/new_agent_policy" + "$ref": "#/components/schemas/agent_policy_update_request" } } } @@ -5956,60 +6066,6 @@ } } }, - "new_agent_policy": { - "title": "New agent policy", - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "namespace": { - "type": "string" - }, - "description": { - "type": "string" - }, - "monitoring_enabled": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "metrics", - "logs" - ] - } - }, - "data_output_id": { - "type": "string", - "nullable": true - }, - "monitoring_output_id": { - "type": "string", - "nullable": true - }, - "fleet_server_host_id": { - "type": "string", - "nullable": true - }, - "download_source_id": { - "type": "string", - "nullable": true - }, - "unenroll_timeout": { - "type": "number" - }, - "inactivity_timeout": { - "type": "number" - } - }, - "required": [ - "name", - "namespace" - ] - }, "new_package_policy": { "title": "New package policy", "type": "object", @@ -6120,83 +6176,238 @@ ] }, "agent_policy": { - "allOf": [ - { - "$ref": "#/components/schemas/new_agent_policy" + "title": "Agent Policy", + "type": "object", + "properties": { + "id": { + "type": "string" }, - { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "status": { - "type": "string", - "enum": [ - "active", - "inactive" - ] - }, - "package_policies": { - "description": "This field is present only when retrieving a single agent policy, or when retrieving a list of agent policy with the ?full=true parameter", - "type": "array", - "items": { - "$ref": "#/components/schemas/package_policy" + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "description": { + "type": "string" + }, + "monitoring_enabled": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "metrics", + "logs" + ] + } + }, + "data_output_id": { + "type": "string", + "nullable": true + }, + "monitoring_output_id": { + "type": "string", + "nullable": true + }, + "fleet_server_host_id": { + "type": "string", + "nullable": true + }, + "download_source_id": { + "type": "string", + "nullable": true + }, + "unenroll_timeout": { + "type": "number" + }, + "inactivity_timeout": { + "type": "number" + }, + "package_policies": { + "description": "This field is present only when retrieving a single agent policy, or when retrieving a list of agent policy with the ?full=true parameter", + "type": "array", + "items": { + "$ref": "#/components/schemas/package_policy" + } + }, + "updated_on": { + "type": "string", + "format": "date-time" + }, + "updated_by": { + "type": "string" + }, + "revision": { + "type": "number" + }, + "agents": { + "type": "number" + }, + "agent_features": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "enabled": { + "type": "boolean" } }, - "updated_on": { - "type": "string", - "format": "date-time" - }, - "updated_by": { - "type": "string" - }, - "data_output_id": { - "type": "string", - "nullable": true - }, - "monitoring_output_id": { - "type": "string", - "nullable": true - }, - "fleet_server_host_id": { - "type": "string", - "nullable": true - }, - "download_source_id": { - "type": "string", - "nullable": true - }, - "revision": { - "type": "number" - }, - "agents": { - "type": "number" + "required": [ + "name", + "enabled" + ] + } + } + }, + "required": [ + "id", + "status", + "name", + "namespace" + ] + }, + "agent_policy_create_request": { + "title": "Create agent policy request", + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "description": { + "type": "string" + }, + "monitoring_enabled": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "metrics", + "logs" + ] + } + }, + "data_output_id": { + "type": "string", + "nullable": true + }, + "monitoring_output_id": { + "type": "string", + "nullable": true + }, + "fleet_server_host_id": { + "type": "string", + "nullable": true + }, + "download_source_id": { + "type": "string", + "nullable": true + }, + "unenroll_timeout": { + "type": "number" + }, + "inactivity_timeout": { + "type": "number" + }, + "agent_features": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "enabled": { + "type": "boolean" + } }, - "agent_features": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "enabled": { - "type": "boolean" - } - }, - "required": [ - "name", - "enabled" - ] + "required": [ + "name", + "enabled" + ] + } + } + }, + "required": [ + "name", + "namespace" + ] + }, + "agent_policy_update_request": { + "title": "Update agent policy request", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "description": { + "type": "string" + }, + "monitoring_enabled": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "metrics", + "logs" + ] + } + }, + "data_output_id": { + "type": "string", + "nullable": true + }, + "monitoring_output_id": { + "type": "string", + "nullable": true + }, + "fleet_server_host_id": { + "type": "string", + "nullable": true + }, + "download_source_id": { + "type": "string", + "nullable": true + }, + "unenroll_timeout": { + "type": "number" + }, + "inactivity_timeout": { + "type": "number" + }, + "agent_features": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "enabled": { + "type": "boolean" } - } - }, - "required": [ - "id", - "status" - ] + }, + "required": [ + "name", + "enabled" + ] + } } + }, + "required": [ + "name", + "namespace" ] }, "full_agent_policy_output": { @@ -6321,6 +6532,12 @@ "verification_mode": { "type": "string" }, + "certificate": { + "type": "string" + }, + "key": { + "type": "string" + }, "certificate_authorities": { "type": "array", "items": { diff --git a/x-pack/plugins/fleet/common/openapi/bundled.yaml b/x-pack/plugins/fleet/common/openapi/bundled.yaml index 417a08dd01d41..5f67c8630b0dc 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.yaml +++ b/x-pack/plugins/fleet/common/openapi/bundled.yaml @@ -155,6 +155,50 @@ paths: operationId: generate-service-token parameters: - $ref: '#/components/parameters/kbn_xsrf' + /epm/verification_key_id: + get: + summary: Get package signature verification key ID + tags: [] + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + body: + type: object + properties: + id: + type: string + nullable: true + description: >- + the key ID of the GPG key used to verify package + signatures + statusCode: + type: number + headers: + type: object + '400': + $ref: '#/components/responses/error' + operationId: packages-get-verification-key-id + parameters: + - schema: + type: string + name: pkgName + in: path + required: true + - schema: + type: string + name: pkgVersion + in: path + required: true + - schema: + type: string + name: filePath + in: path + required: true /epm/categories: get: summary: Package categories @@ -1257,7 +1301,7 @@ paths: name: agentId in: path required: true - put: + post: summary: Agent - Reassign tags: [] responses: @@ -1283,6 +1327,33 @@ paths: type: string required: - policy_id + put: + summary: Agent - Reassign + tags: [] + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + '400': + $ref: '#/components/responses/error' + operationId: reassign-agent-deprecated + parameters: + - $ref: '#/components/parameters/kbn_xsrf' + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + policy_id: + type: string + required: + - policy_id + deprecated: true /agents/{agentId}/unenroll: parameters: - schema: @@ -1675,7 +1746,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/new_agent_policy' + $ref: '#/components/schemas/agent_policy_create_request' security: [] parameters: - $ref: '#/components/parameters/kbn_xsrf' @@ -1728,7 +1799,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/new_agent_policy' + $ref: '#/components/schemas/agent_policy_update_request' parameters: - $ref: '#/components/parameters/kbn_xsrf' /agent_policies/{agentPolicyId}/copy: @@ -3797,44 +3868,6 @@ components: type: array items: type: string - new_agent_policy: - title: New agent policy - type: object - properties: - id: - type: string - name: - type: string - namespace: - type: string - description: - type: string - monitoring_enabled: - type: array - items: - type: string - enum: - - metrics - - logs - data_output_id: - type: string - nullable: true - monitoring_output_id: - type: string - nullable: true - fleet_server_host_id: - type: string - nullable: true - download_source_id: - type: string - nullable: true - unenroll_timeout: - type: number - inactivity_timeout: - type: number - required: - - name - - namespace new_package_policy: title: New package policy type: object @@ -3908,61 +3941,171 @@ components: - revision - $ref: '#/components/schemas/new_package_policy' agent_policy: - allOf: - - $ref: '#/components/schemas/new_agent_policy' - - type: object - properties: - id: - type: string - status: - type: string - enum: - - active - - inactive - package_policies: - description: >- - This field is present only when retrieving a single agent - policy, or when retrieving a list of agent policy with the - ?full=true parameter - type: array - items: - $ref: '#/components/schemas/package_policy' - updated_on: - type: string - format: date-time - updated_by: - type: string - data_output_id: - type: string - nullable: true - monitoring_output_id: - type: string - nullable: true - fleet_server_host_id: - type: string - nullable: true - download_source_id: - type: string - nullable: true - revision: - type: number - agents: - type: number - agent_features: - type: array - items: - type: object - properties: - name: - type: string - enabled: - type: boolean - required: - - name - - enabled - required: - - id - - status + title: Agent Policy + type: object + properties: + id: + type: string + name: + type: string + namespace: + type: string + description: + type: string + monitoring_enabled: + type: array + items: + type: string + enum: + - metrics + - logs + data_output_id: + type: string + nullable: true + monitoring_output_id: + type: string + nullable: true + fleet_server_host_id: + type: string + nullable: true + download_source_id: + type: string + nullable: true + unenroll_timeout: + type: number + inactivity_timeout: + type: number + package_policies: + description: >- + This field is present only when retrieving a single agent policy, or + when retrieving a list of agent policy with the ?full=true parameter + type: array + items: + $ref: '#/components/schemas/package_policy' + updated_on: + type: string + format: date-time + updated_by: + type: string + revision: + type: number + agents: + type: number + agent_features: + type: array + items: + type: object + properties: + name: + type: string + enabled: + type: boolean + required: + - name + - enabled + required: + - id + - status + - name + - namespace + agent_policy_create_request: + title: Create agent policy request + type: object + properties: + id: + type: string + name: + type: string + namespace: + type: string + description: + type: string + monitoring_enabled: + type: array + items: + type: string + enum: + - metrics + - logs + data_output_id: + type: string + nullable: true + monitoring_output_id: + type: string + nullable: true + fleet_server_host_id: + type: string + nullable: true + download_source_id: + type: string + nullable: true + unenroll_timeout: + type: number + inactivity_timeout: + type: number + agent_features: + type: array + items: + type: object + properties: + name: + type: string + enabled: + type: boolean + required: + - name + - enabled + required: + - name + - namespace + agent_policy_update_request: + title: Update agent policy request + type: object + properties: + name: + type: string + namespace: + type: string + description: + type: string + monitoring_enabled: + type: array + items: + type: string + enum: + - metrics + - logs + data_output_id: + type: string + nullable: true + monitoring_output_id: + type: string + nullable: true + fleet_server_host_id: + type: string + nullable: true + download_source_id: + type: string + nullable: true + unenroll_timeout: + type: number + inactivity_timeout: + type: number + agent_features: + type: array + items: + type: object + properties: + name: + type: string + enabled: + type: boolean + required: + - name + - enabled + required: + - name + - namespace full_agent_policy_output: title: Full agent policy type: object @@ -4048,6 +4191,10 @@ components: properties: verification_mode: type: string + certificate: + type: string + key: + type: string certificate_authorities: type: array items: diff --git a/x-pack/plugins/fleet/common/openapi/components/schemas/agent_policy.yaml b/x-pack/plugins/fleet/common/openapi/components/schemas/agent_policy.yaml index 76b6fba16c873..3f56db4174216 100644 --- a/x-pack/plugins/fleet/common/openapi/components/schemas/agent_policy.yaml +++ b/x-pack/plugins/fleet/common/openapi/components/schemas/agent_policy.yaml @@ -1,52 +1,65 @@ -allOf: - - $ref: ./new_agent_policy.yaml - - type: object - properties: - id: - type: string - status: - type: string - enum: - - active - - inactive - package_policies: - description: This field is present only when retrieving a single agent policy, or when retrieving a list of agent policy with the ?full=true parameter - type: array - items: - $ref: ./package_policy.yaml - updated_on: - type: string - format: date-time - updated_by: - type: string - data_output_id: - type: string - nullable: true - monitoring_output_id: - type: string - nullable: true - fleet_server_host_id: - type: string - nullable: true - download_source_id: - type: string - nullable: true - revision: - type: number - agents: - type: number - agent_features: - type: array - items: - type: object - properties: - name: - type: string - enabled: - type: boolean - required: - - name - - enabled - required: - - id - - status +title: Agent Policy +type: object +properties: + id: + type: string + name: + type: string + namespace: + type: string + description: + type: string + monitoring_enabled: + type: array + items: + type: string + enum: + - metrics + - logs + data_output_id: + type: string + nullable: true + monitoring_output_id: + type: string + nullable: true + fleet_server_host_id: + type: string + nullable: true + download_source_id: + type: string + nullable: true + unenroll_timeout: + type: number + inactivity_timeout: + type: number + package_policies: + description: This field is present only when retrieving a single agent policy, or when retrieving a list of agent policy with the ?full=true parameter + type: array + items: + $ref: ./package_policy.yaml + updated_on: + type: string + format: date-time + updated_by: + type: string + revision: + type: number + agents: + type: number + agent_features: + type: array + items: + type: object + properties: + name: + type: string + enabled: + type: boolean + required: + - name + - enabled +required: + - id + - status + - name + - namespace diff --git a/x-pack/plugins/fleet/common/openapi/components/schemas/new_agent_policy.yaml b/x-pack/plugins/fleet/common/openapi/components/schemas/agent_policy_create_request.yaml similarity index 70% rename from x-pack/plugins/fleet/common/openapi/components/schemas/new_agent_policy.yaml rename to x-pack/plugins/fleet/common/openapi/components/schemas/agent_policy_create_request.yaml index 2edab88c70490..fe9fccf1429bb 100644 --- a/x-pack/plugins/fleet/common/openapi/components/schemas/new_agent_policy.yaml +++ b/x-pack/plugins/fleet/common/openapi/components/schemas/agent_policy_create_request.yaml @@ -1,4 +1,4 @@ -title: New agent policy +title: Create agent policy request type: object properties: id: @@ -32,6 +32,18 @@ properties: type: number inactivity_timeout: type: number + agent_features: + type: array + items: + type: object + properties: + name: + type: string + enabled: + type: boolean + required: + - name + - enabled required: - name - namespace diff --git a/x-pack/plugins/fleet/common/openapi/components/schemas/agent_policy_update_request.yaml b/x-pack/plugins/fleet/common/openapi/components/schemas/agent_policy_update_request.yaml new file mode 100644 index 0000000000000..ea951e0e4e8c4 --- /dev/null +++ b/x-pack/plugins/fleet/common/openapi/components/schemas/agent_policy_update_request.yaml @@ -0,0 +1,47 @@ +title: Update agent policy request +type: object +properties: + name: + type: string + namespace: + type: string + description: + type: string + monitoring_enabled: + type: array + items: + type: string + enum: + - metrics + - logs + data_output_id: + type: string + nullable: true + monitoring_output_id: + type: string + nullable: true + fleet_server_host_id: + type: string + nullable: true + download_source_id: + type: string + nullable: true + unenroll_timeout: + type: number + inactivity_timeout: + type: number + agent_features: + type: array + items: + type: object + properties: + name: + type: string + enabled: + type: boolean + required: + - name + - enabled +required: + - name + - namespace diff --git a/x-pack/plugins/fleet/common/openapi/components/schemas/full_agent_policy.yaml b/x-pack/plugins/fleet/common/openapi/components/schemas/full_agent_policy.yaml index ab981d5457666..4086de156c594 100644 --- a/x-pack/plugins/fleet/common/openapi/components/schemas/full_agent_policy.yaml +++ b/x-pack/plugins/fleet/common/openapi/components/schemas/full_agent_policy.yaml @@ -32,6 +32,10 @@ properties: properties: verification_mode: type: string + certificate: + type: string + key: + type: string certificate_authorities: type: array items: diff --git a/x-pack/plugins/fleet/common/openapi/entrypoint.yaml b/x-pack/plugins/fleet/common/openapi/entrypoint.yaml index 4ff56806eb0ef..37c33c28b1053 100644 --- a/x-pack/plugins/fleet/common/openapi/entrypoint.yaml +++ b/x-pack/plugins/fleet/common/openapi/entrypoint.yaml @@ -26,6 +26,8 @@ paths: /service_tokens: $ref: paths/service_tokens.yaml # EPM / integrations endpoints + /epm/verification_key_id: + $ref: paths/epm@verification_key_id.yaml /epm/categories: $ref: paths/epm@categories.yaml /epm/packages/limited: diff --git a/x-pack/plugins/fleet/common/openapi/paths/agent_policies.yaml b/x-pack/plugins/fleet/common/openapi/paths/agent_policies.yaml index dfe49938c208a..33fd8a2348412 100644 --- a/x-pack/plugins/fleet/common/openapi/paths/agent_policies.yaml +++ b/x-pack/plugins/fleet/common/openapi/paths/agent_policies.yaml @@ -40,7 +40,7 @@ get: type: boolean in: query name: noAgentCount - description: When set to true, do not count how many agents are in the agent policy, this can improve performance if you are searching over a large number of agent policies. The "agents" property will always be 0 if set to true. + description: When set to true, do not count how many agents are in the agent policy, this can improve performance if you are searching over a large number of agent policies. The "agents" property will always be 0 if set to true. description: '' post: @@ -63,7 +63,7 @@ post: content: application/json: schema: - $ref: ../components/schemas/new_agent_policy.yaml + $ref: ../components/schemas/agent_policy_create_request.yaml security: [] parameters: - $ref: ../components/headers/kbn_xsrf.yaml diff --git a/x-pack/plugins/fleet/common/openapi/paths/agent_policies@{agent_policy_id}.yaml b/x-pack/plugins/fleet/common/openapi/paths/agent_policies@{agent_policy_id}.yaml index 0bdcbf38a70fa..4a7e88abcbab8 100644 --- a/x-pack/plugins/fleet/common/openapi/paths/agent_policies@{agent_policy_id}.yaml +++ b/x-pack/plugins/fleet/common/openapi/paths/agent_policies@{agent_policy_id}.yaml @@ -46,6 +46,6 @@ put: content: application/json: schema: - $ref: ../components/schemas/new_agent_policy.yaml + $ref: ../components/schemas/agent_policy_update_request.yaml parameters: - $ref: ../components/headers/kbn_xsrf.yaml diff --git a/x-pack/plugins/fleet/common/openapi/paths/agents@{agent_id}@reassign.yaml b/x-pack/plugins/fleet/common/openapi/paths/agents@{agent_id}@reassign.yaml index 4827cc77fc634..af00d1563854e 100644 --- a/x-pack/plugins/fleet/common/openapi/paths/agents@{agent_id}@reassign.yaml +++ b/x-pack/plugins/fleet/common/openapi/paths/agents@{agent_id}@reassign.yaml @@ -4,7 +4,7 @@ parameters: name: agentId in: path required: true -put: +post: summary: Agent - Reassign tags: [] responses: @@ -30,4 +30,31 @@ put: type: string required: - policy_id +put: + summary: Agent - Reassign + tags: [] + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + '400': + $ref: ../components/responses/error.yaml + operationId: reassign-agent-deprecated + parameters: + - $ref: ../components/headers/kbn_xsrf.yaml + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + policy_id: + type: string + required: + - policy_id + deprecated: true diff --git a/x-pack/plugins/fleet/common/openapi/paths/epm@verification_key_id.yaml b/x-pack/plugins/fleet/common/openapi/paths/epm@verification_key_id.yaml new file mode 100644 index 0000000000000..c9216b85cd78f --- /dev/null +++ b/x-pack/plugins/fleet/common/openapi/paths/epm@verification_key_id.yaml @@ -0,0 +1,41 @@ +get: + summary: Get package signature verification key ID + tags: [] + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + body: + type: object + properties: + id: + type: string + nullable: true + description: the key ID of the GPG key used to verify package signatures + statusCode: + type: number + headers: + type: object + '400': + $ref: ../components/responses/error.yaml + operationId: packages-get-verification-key-id +parameters: + - schema: + type: string + name: pkgName + in: path + required: true + - schema: + type: string + name: pkgVersion + in: path + required: true + - schema: + type: string + name: filePath + in: path + required: true diff --git a/x-pack/plugins/fleet/common/services/policy_template.ts b/x-pack/plugins/fleet/common/services/policy_template.ts index c8fa687123fc6..f77aaae664f29 100644 --- a/x-pack/plugins/fleet/common/services/policy_template.ts +++ b/x-pack/plugins/fleet/common/services/policy_template.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { DATASET_VAR_NAME } from '../constants'; import type { RegistryPolicyTemplate, RegistryPolicyInputOnlyTemplate, @@ -17,7 +18,7 @@ import type { } from '../types'; const DATA_STREAM_DATASET_VAR: RegistryVarsEntry = { - name: 'data_stream.dataset', + name: DATASET_VAR_NAME, type: 'text', title: 'Dataset name', description: diff --git a/x-pack/plugins/fleet/common/services/routes.ts b/x-pack/plugins/fleet/common/services/routes.ts index 3e6462bdba4d7..0321308e51c82 100644 --- a/x-pack/plugins/fleet/common/services/routes.ts +++ b/x-pack/plugins/fleet/common/services/routes.ts @@ -26,6 +26,10 @@ import { } from '../constants'; export const epmRouteService = { + getVerificationKeyIdPath: () => { + return EPM_API_ROUTES.VERIFICATION_KEY_ID; + }, + getCategoriesPath: () => { return EPM_API_ROUTES.CATEGORIES_PATTERN; }, diff --git a/x-pack/plugins/fleet/common/types/models/agent_policy.ts b/x-pack/plugins/fleet/common/types/models/agent_policy.ts index 465d6adcb1acd..2f0f5adae5978 100644 --- a/x-pack/plugins/fleet/common/types/models/agent_policy.ts +++ b/x-pack/plugins/fleet/common/types/models/agent_policy.ts @@ -134,6 +134,8 @@ export interface FullAgentPolicyFleetConfig { verification_mode?: string; certificate_authorities?: string[]; renegotiation?: string; + certificate?: string; + key?: string; }; } diff --git a/x-pack/plugins/fleet/common/types/rest_spec/agent.ts b/x-pack/plugins/fleet/common/types/rest_spec/agent.ts index 7a73829838d55..603eeed136059 100644 --- a/x-pack/plugins/fleet/common/types/rest_spec/agent.ts +++ b/x-pack/plugins/fleet/common/types/rest_spec/agent.ts @@ -119,15 +119,25 @@ export type PostBulkAgentUpgradeResponse = BulkAgentAction; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface PostAgentUpgradeResponse {} +// deprecated export interface PutAgentReassignRequest { params: { agentId: string; }; body: { policy_id: string }; } - +// deprecated // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface PutAgentReassignResponse {} +export interface PostAgentReassignRequest { + params: { + agentId: string; + }; + body: { policy_id: string }; +} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface PostAgentReassignResponse {} export interface PostBulkAgentReassignRequest { body: { diff --git a/x-pack/plugins/fleet/common/types/rest_spec/epm.ts b/x-pack/plugins/fleet/common/types/rest_spec/epm.ts index 105558e0d0620..d7321d8218590 100644 --- a/x-pack/plugins/fleet/common/types/rest_spec/epm.ts +++ b/x-pack/plugins/fleet/common/types/rest_spec/epm.ts @@ -171,3 +171,6 @@ export interface DeletePackageResponse { response?: AssetReference[]; items: AssetReference[]; } +export interface GetVerificationKeyIdResponse { + id: string | null; +} diff --git a/x-pack/plugins/fleet/kibana.jsonc b/x-pack/plugins/fleet/kibana.jsonc index bf9db5435be82..4a8076fd09836 100644 --- a/x-pack/plugins/fleet/kibana.jsonc +++ b/x-pack/plugins/fleet/kibana.jsonc @@ -17,7 +17,6 @@ "navigation", "customIntegrations", "share", - "spaces", "security", "unifiedSearch", "savedObjectsTagging", @@ -33,7 +32,8 @@ "globalSearch", "telemetry", "discover", - "ingestPipelines" + "ingestPipelines", + "spaces", ], "requiredBundles": [ "kibanaReact", diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/advanced_tab.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/advanced_tab.tsx index 79582e94a6226..b9135b3dc716b 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/advanced_tab.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/advanced_tab.tsx @@ -5,7 +5,7 @@ * 2.0. */ import React from 'react'; -import { EuiLoadingContent, EuiSteps } from '@elastic/eui'; +import { EuiSkeletonText, EuiSteps } from '@elastic/eui'; import { useAdvancedForm } from './hooks'; import { useLatestFleetServers } from './hooks/use_latest_fleet_servers'; @@ -83,7 +83,7 @@ export const AdvancedTab: React.FunctionComponent = ({ ]; return isSelectFleetServerPolicyLoading ? ( - + ) : ( ); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_stream.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_stream.tsx index 50b585cac9c89..06059eac6a671 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_stream.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_stream.tsx @@ -21,6 +21,8 @@ import { } from '@elastic/eui'; import { useRouteMatch } from 'react-router-dom'; +import { DATASET_VAR_NAME } from '../../../../../../../../../common/constants'; + import { useConfig, useGetDataStreams } from '../../../../../../../../hooks'; import { mapPackageReleaseToIntegrationCardRelease } from '../../../../../../../../../common/services'; @@ -90,6 +92,10 @@ export const PackagePolicyInputStreamConfig = memo( const isPackagePolicyEdit = !!packagePolicyId; const isInputOnlyPackage = packageInfo.type === 'input'; + const hasDatasetVar = packageInputStream.vars?.some( + (varDef) => varDef.name === DATASET_VAR_NAME + ); + const showPipelinesAndMappings = !isInputOnlyPackage && !hasDatasetVar; useEffect(() => { if (isDefaultDatastream && containerRef.current) { containerRef.current.scrollIntoView(); @@ -297,7 +303,7 @@ export const PackagePolicyInputStreamConfig = memo( ); })} {/* Only show datastream pipelines and mappings on edit and not for input packages*/} - {isPackagePolicyEdit && !isInputOnlyPackage && ( + {isPackagePolicyEdit && showPipelinesAndMappings && ( <> ); } - if (name === 'data_stream.dataset' && packageType === 'input') { + if (name === DATASET_VAR_NAME && packageType === 'input') { return ( { - // TODO move this to parent hook - // If agent policy has changed, update package policy's agent policy ID and namespace - if (agentPolicy && packagePolicy.policy_id !== agentPolicy.id) { - updatePackagePolicy({ - policy_id: agentPolicy.id, - namespace: agentPolicy.namespace, - }); - } - }, [packagePolicy, agentPolicy, packageInfo, updatePackagePolicy]); - const isManaged = packagePolicy.is_managed; return validationResults ? ( diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/confirm_incoming_data_with_preview.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/confirm_incoming_data_with_preview.tsx index 772d90ada7dc1..5e6443986c7c6 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/confirm_incoming_data_with_preview.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/confirm_incoming_data_with_preview.tsx @@ -11,13 +11,13 @@ import { EuiText, EuiSpacer, EuiLink, - EuiLoadingContent, EuiLoadingSpinner, EuiHorizontalRule, EuiFlexGroup, EuiFlexItem, formatDate, EuiDescriptionList, + EuiSkeletonText, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; @@ -188,7 +188,7 @@ export const ConfirmIncomingDataWithPreview: React.FunctionComponent = ({ )} - + ); } diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/services/prepare_input_pkg_policy_dataset.ts b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/services/prepare_input_pkg_policy_dataset.ts index e4f1fae4419c1..413368772101a 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/services/prepare_input_pkg_policy_dataset.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/services/prepare_input_pkg_policy_dataset.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { DATASET_VAR_NAME } from '../../../../../../../common/constants'; + import type { NewPackagePolicy } from '../../../../types'; export function prepareInputPackagePolicyDataset(newPolicy: NewPackagePolicy): { @@ -27,16 +29,16 @@ export function prepareInputPackagePolicyDataset(newPolicy: NewPackagePolicy): { const newStreams = streams.map((stream) => { if ( !stream.vars || - !stream.vars['data_stream.dataset'] || - !stream.vars['data_stream.dataset'].value?.package + !stream.vars[DATASET_VAR_NAME] || + !stream.vars[DATASET_VAR_NAME].value?.package ) { return stream; } - const datasetVar = stream.vars['data_stream.dataset']; + const datasetVar = stream.vars[DATASET_VAR_NAME]; forceCreateNeeded = datasetVar.value?.package !== newPolicy?.package?.name; - stream.vars['data_stream.dataset'] = { + stream.vars[DATASET_VAR_NAME] = { ...datasetVar, value: datasetVar.value?.dataset, }; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.test.tsx index d57b8cff64a84..3128bec1a8bbf 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.test.tsx @@ -10,7 +10,7 @@ import type { RenderHookResult } from '@testing-library/react-hooks'; import type { TestRenderer } from '../../../../../../../mock'; import { createFleetTestRendererMock } from '../../../../../../../mock'; -import type { PackageInfo } from '../../../../../types'; +import type { AgentPolicy, PackageInfo } from '../../../../../types'; import { sendGetPackagePolicies } from '../../../../../hooks'; @@ -94,6 +94,43 @@ describe('useOnSubmit', () => { }); }); + it('should set package policy id and namespace when agent policy changes', () => { + act(() => { + renderResult.result.current.updateAgentPolicy({ + id: 'some-id', + namespace: 'default', + } as AgentPolicy); + }); + + expect(renderResult.result.current.packagePolicy).toEqual({ + policy_id: 'some-id', + namespace: 'default', + description: '', + enabled: true, + inputs: [], + name: 'apache-1', + package: { + name: 'apache', + title: 'Apache', + version: '1.0.0', + }, + vars: { + 'Advanced var': { + type: 'bool', + value: true, + }, + 'Required var': { + type: 'bool', + value: undefined, + }, + 'Show user var': { + type: 'string', + value: 'showUserVarVal', + }, + }, + }); + }); + it('should set index 1 name to package policy on init if no package policies exist for this package', () => { // waitFor(() => { // expect(renderResult.getByDisplayValue('apache-1')).toBeInTheDocument(); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx index 8d7259a77767f..9e77b746214e1 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx @@ -219,6 +219,15 @@ export function useOnSubmit({ init(); }, [packageInfo, agentPolicy, updatePackagePolicy, integrationToEnable, isInitialized]); + useEffect(() => { + if (agentPolicy && packagePolicy.policy_id !== agentPolicy.id) { + updatePackagePolicy({ + policy_id: agentPolicy.id, + namespace: agentPolicy.namespace, + }); + } + }, [packagePolicy, agentPolicy, updatePackagePolicy]); + const onSaveNavigate = useOnSaveNavigate({ packagePolicy, queryParamsPolicyId, diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx index 5ab6a7b0938d5..2898b1e241f1e 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx @@ -278,18 +278,23 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({ ); } - const replaceStepConfigurePackagePolicy = replaceDefineStepView && packageInfo?.name && ( - - - - ); + const replaceStepConfigurePackagePolicy = + replaceDefineStepView && packageInfo?.name ? ( + !isInitialized ? ( + + ) : ( + + + + ) + ) : undefined; const stepConfigurePackagePolicy = useMemo( () => diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/services/devtools_request.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/services/devtools_request.tsx index b9c69d083d21d..a4543005cc9b9 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/services/devtools_request.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/services/devtools_request.tsx @@ -7,6 +7,8 @@ import { omit } from 'lodash'; +import { DATASET_VAR_NAME } from '../../../../../../common/constants'; + import { agentPolicyRouteService, packagePolicyRouteService } from '../../../services'; import { generateInputId } from '../../../../../../common/services/simplified_package_policy_helper'; import type { @@ -105,8 +107,8 @@ function formatVars(vars: NewPackagePolicy['inputs'][number]['vars']) { } return Object.entries(vars).reduce((acc, [varKey, varRecord]) => { - // the data_stream.dataset var uses an internal format before we send it - if (varKey === 'data_stream.dataset' && varRecord?.value?.dataset) { + // the dataset var uses an internal format before we send it + if (varKey === DATASET_VAR_NAME && varRecord?.value?.dataset) { acc[varKey] = varRecord?.value.dataset; } else { acc[varKey] = varRecord?.value; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_details/agent_details_overview.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_details/agent_details_overview.tsx index 5f0b64051f26f..4459b8f762382 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_details/agent_details_overview.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_details/agent_details_overview.tsx @@ -15,8 +15,8 @@ import { EuiFlexItem, EuiPanel, EuiIcon, + EuiSkeletonText, EuiToolTip, - EuiLoadingContent, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage, FormattedRelative } from '@kbn/i18n-react'; @@ -52,15 +52,47 @@ export const AgentDetailsOverviewSection: React.FunctionComponent<{ {[ { - title: i18n.translate('xpack.fleet.agentDetails.cpuLabel', { - defaultMessage: 'CPU', - }), + title: ( + + } + > + + +   + + + + ), description: formatAgentCPU(agent.metrics, agentPolicy), }, { - title: i18n.translate('xpack.fleet.agentDetails.memoryLabel', { - defaultMessage: 'Memory', - }), + title: ( + + } + > + + +   + + + + ), description: formatAgentMemory(agent.metrics, agentPolicy), }, ].map(({ title, description }) => { @@ -128,7 +160,7 @@ export const AgentDetailsOverviewSection: React.FunctionComponent<{ description: agentPolicy ? ( ) : ( - + ), }, { @@ -213,7 +245,7 @@ export const AgentDetailsOverviewSection: React.FunctionComponent<{ /> ) ) : ( - + ), }, { @@ -233,7 +265,7 @@ export const AgentDetailsOverviewSection: React.FunctionComponent<{ /> ) ) : ( - + ), }, { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_diagnostics/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_diagnostics/index.tsx index 180a1b894ca13..7ec0de9023e32 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_diagnostics/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_diagnostics/index.tsx @@ -15,9 +15,9 @@ import { EuiFlexItem, EuiIcon, EuiLink, - EuiLoadingContent, EuiLoadingSpinner, EuiText, + EuiSkeletonText, formatDate, } from '@elastic/eui'; import React, { useCallback, useEffect, useState } from 'react'; @@ -272,7 +272,7 @@ export const AgentDiagnosticsTab: React.FunctionComponent {isLoading ? ( - + ) : ( items={diagnosticsEntries} columns={columns} /> )} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/constants.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/constants.tsx index 9f5cf41ecbcde..bee26f7c5b067 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/constants.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/constants.tsx @@ -5,6 +5,8 @@ * 2.0. */ +import { DATASET_VAR_NAME } from '../../../../../../../../common/constants'; + import type { AgentLogsState } from './agent_logs'; export const AGENT_LOG_INDEX_PATTERN = 'logs-elastic_agent-*,logs-elastic_agent.*-*'; @@ -21,7 +23,7 @@ export const AGENT_ID_FIELD = { type: 'string', }; export const DATASET_FIELD = { - name: 'data_stream.dataset', + name: DATASET_VAR_NAME, type: 'string', aggregatable: true, searchable: true, diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_reassign_policy_modal/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_reassign_policy_modal/index.tsx index cb3f0d77eed34..5fd6183863fb6 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_reassign_policy_modal/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_reassign_policy_modal/index.tsx @@ -19,7 +19,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import type { Agent } from '../../../../types'; import { - sendPutAgentReassign, + sendPostAgentReassign, sendPostBulkAgentReassign, useStartServices, useGetAgentPolicies, @@ -71,7 +71,7 @@ export const AgentReassignAgentPolicyModal: React.FunctionComponent = ({ throw new Error('No selected agent policy id'); } const res = isSingleAgent - ? await sendPutAgentReassign((agents[0] as Agent).id, { + ? await sendPostAgentReassign((agents[0] as Agent).id, { policy_id: selectedAgentPolicyId, }) : await sendPostBulkAgentReassign({ diff --git a/x-pack/plugins/fleet/public/applications/integrations/hooks/use_package_install.tsx b/x-pack/plugins/fleet/public/applications/integrations/hooks/use_package_install.tsx index 101de2a30cef5..74580fede42bf 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/hooks/use_package_install.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/hooks/use_package_install.tsx @@ -133,7 +133,7 @@ function usePackageInstall({ />, { theme$ } ), - iconType: 'alert', + iconType: 'error', }); } else { setPackageInstallStatus({ name, status: InstallStatus.installed, version }); @@ -224,7 +224,7 @@ function usePackageInstall({ />, { theme$ } ), - iconType: 'alert', + iconType: 'error', }); } else { setPackageInstallStatus({ name, status: InstallStatus.notInstalled, version: null }); diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/index.tsx index 67ea0781bafc2..ffbc34ab2c55e 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/index.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/index.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { Switch } from 'react-router-dom'; import { Route } from '@kbn/shared-ux-router'; -import { EuiLoadingContent } from '@elastic/eui'; +import { EuiSkeletonText } from '@elastic/eui'; import { INTEGRATIONS_ROUTING_PATHS } from '../../constants'; import { IntegrationsStateContextProvider, useBreadcrumbs } from '../../hooks'; @@ -34,7 +34,7 @@ export const EPMApp: React.FunctionComponent = () => { - }> + }> diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.test.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.test.tsx index 87ce388fa9db5..09e8a7c90750e 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.test.tsx @@ -39,7 +39,8 @@ import { ExperimentalFeaturesService } from '../../../../services'; ExperimentalFeaturesService.init({}); import { Detail } from '.'; -describe('when on integration detail', () => { +// FLAKY: https://github.com/elastic/kibana/issues/150607 +describe.skip('when on integration detail', () => { const pkgkey = 'nginx-0.3.7'; const detailPageUrlPath = pagePathGetters.integration_details_overview({ pkgkey })[1]; let testRenderer: TestRenderer; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/details.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/details.tsx index 94a2a223b122a..8100f07e192ea 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/details.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/details.tsx @@ -32,6 +32,8 @@ import { entries } from '../../../../../types'; import { useGetCategoriesQuery } from '../../../../../hooks'; import { AssetTitleMap, DisplayedAssets, ServiceTitleMap } from '../../../constants'; +import { ChangelogModal } from '../settings/changelog_modal'; + import { NoticeModal } from './notice_modal'; import { LicenseModal } from './license_modal'; @@ -71,13 +73,18 @@ export const Details: React.FC = memo(({ packageInfo }) => { const [isNoticeModalOpen, setIsNoticeModalOpen] = useState(false); const toggleNoticeModal = useCallback(() => { - setIsNoticeModalOpen(!isNoticeModalOpen); - }, [isNoticeModalOpen]); + setIsNoticeModalOpen((currentState) => !currentState); + }, []); const [isLicenseModalOpen, setIsLicenseModalOpen] = useState(false); const toggleLicenseModal = useCallback(() => { - setIsLicenseModalOpen(!isLicenseModalOpen); - }, [isLicenseModalOpen]); + setIsLicenseModalOpen((currentState) => !currentState); + }, []); + + const [isChangelogModalOpen, setIsChangelogModalOpen] = useState(false); + const toggleChangelogModal = useCallback(() => { + setIsChangelogModalOpen((currentState) => !currentState); + }, []); const listItems = useMemo(() => { // Base details: version and categories @@ -201,6 +208,21 @@ export const Details: React.FC = memo(({ packageInfo }) => { }); } + items.push({ + title: ( + + + + ), + description: ( + <> +

    + View Changelog +

    + + ), + }); + return items; }, [ packageCategories, @@ -214,6 +236,7 @@ export const Details: React.FC = memo(({ packageInfo }) => { packageInfo.version, toggleLicenseModal, toggleNoticeModal, + toggleChangelogModal, ]); return ( @@ -232,6 +255,15 @@ export const Details: React.FC = memo(({ packageInfo }) => { /> )} + + {isChangelogModalOpen && ( + + )} + diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/notice_modal.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/notice_modal.tsx index 4ef6dde3fb2f6..a4ab3c19200bc 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/notice_modal.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/notice_modal.tsx @@ -8,13 +8,13 @@ import React, { useEffect, useState } from 'react'; import { EuiCodeBlock, - EuiLoadingContent, EuiModal, EuiModalBody, EuiModalHeader, EuiModalFooter, EuiModalHeaderTitle, EuiButton, + EuiSkeletonText, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -58,10 +58,10 @@ export const NoticeModal: React.FunctionComponent = ({ noticePath, onClos // Simulate a long notice while loading <>

    - +

    - +

    )} diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx index 53806c5b4d98c..888903a293e14 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx @@ -15,7 +15,11 @@ import { isPackagePrerelease, } from '../../../../../../../../common/services'; -import { useFleetStatus, useLink, useStartServices } from '../../../../../../../hooks'; +import { + useGetPackageVerificationKeyId, + useLink, + useStartServices, +} from '../../../../../../../hooks'; import { isPackageUnverified } from '../../../../../../../services'; import type { PackageInfo, RegistryPolicyTemplate } from '../../../../../types'; @@ -115,7 +119,7 @@ export const OverviewPage: React.FC = memo( () => integrationInfo?.screenshots || packageInfo.screenshots || [], [integrationInfo, packageInfo.screenshots] ); - const { packageVerificationKeyId } = useFleetStatus(); + const { packageVerificationKeyId } = useGetPackageVerificationKeyId(); const isUnverified = isPackageUnverified(packageInfo, packageVerificationKeyId); const isPrerelease = isPackagePrerelease(packageInfo.version); return ( diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/readme.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/readme.tsx index 582bbb6dfd31e..52b4f8fbe4fdc 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/readme.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/readme.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { EuiLoadingContent, EuiText } from '@elastic/eui'; +import { EuiText, EuiSkeletonText } from '@elastic/eui'; import React, { Fragment, useEffect, useState } from 'react'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; @@ -57,13 +57,13 @@ export function Readme({ {/* simulates a long page of text loading */}

    - +

    - +

    - +

    )} diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/changelog_modal.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/changelog_modal.tsx new file mode 100644 index 0000000000000..119a236c18b7a --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/changelog_modal.tsx @@ -0,0 +1,92 @@ +/* + * Copyright 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 { + EuiCodeBlock, + EuiModal, + EuiModalBody, + EuiModalHeader, + EuiModalFooter, + EuiModalHeaderTitle, + EuiButton, + EuiSkeletonText, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import { useGetFileByPathQuery, useStartServices } from '../../../../../hooks'; + +import { getFormattedChangelog } from '../utils'; + +export interface ChangeLogParams { + version: string; + changes: Array<{ + description: string; + link: string; + type: string; + }>; +} + +interface Props { + latestVersion: string; + currentVersion?: string; + packageName: string; + onClose: () => void; +} + +export const ChangelogModal: React.FunctionComponent = ({ + latestVersion, + currentVersion, + packageName, + onClose, +}) => { + const { notifications } = useStartServices(); + + const { + data: changelogResponse, + error: changelogError, + isLoading, + } = useGetFileByPathQuery(`/package/${packageName}/${latestVersion}/changelog.yml`); + const changelogText = changelogResponse?.data; + + // currentVersion is used to display the changelog up to the current installed version, when there is a newer one available + const finalChangelog = currentVersion + ? getFormattedChangelog(changelogText, latestVersion, currentVersion) + : getFormattedChangelog(changelogText, latestVersion); + + if (changelogError) { + notifications.toasts.addError(changelogError, { + title: i18n.translate('xpack.fleet.epm.errorLoadingChangelog', { + defaultMessage: 'Error loading changelog information', + }), + }); + } + + return ( + + + {'Changelog'} + + + + {finalChangelog} + + + + + + + + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx index 966b0419285ec..b7ec3019b66a6 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx @@ -18,6 +18,7 @@ import { EuiText, EuiSpacer, EuiLink, + EuiPortal, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -48,6 +49,7 @@ import { InstallButton } from './install_button'; import { ReinstallButton } from './reinstall_button'; import { UpdateButton } from './update_button'; import { UninstallButton } from './uninstall_button'; +import { ChangelogModal } from './changelog_modal'; const SettingsTitleCell = styled.td` padding-right: ${(props) => props.theme.eui.euiSizeXL}; @@ -62,7 +64,13 @@ const NoteLabel = () => ( /> ); -const UpdatesAvailableMsg = ({ latestVersion }: { latestVersion: string }) => ( +const UpdatesAvailableMsg = ({ + latestVersion, + toggleChangelogModal, +}: { + latestVersion: string; + toggleChangelogModal: () => void; +}) => ( ( defaultMessage: 'New version available', })} > - + + + + + +

    + {'View changelog.'} +

    +
    +
    ); @@ -101,6 +118,11 @@ interface Props { export const SettingsPage: React.FC = memo(({ packageInfo, theme$ }: Props) => { const { name, title, latestVersion, version, keepPoliciesUpToDate } = packageInfo; const [isUpgradingPackagePolicies, setIsUpgradingPackagePolicies] = useState(false); + const [isChangelogModalOpen, setIsChangelogModalOpen] = useState(false); + + const toggleChangelogModal = useCallback(() => { + setIsChangelogModalOpen(!isChangelogModalOpen); + }, [isChangelogModalOpen]); const getPackageInstallStatus = useGetPackageInstallStatus(); const { data: packagePoliciesData } = useGetPackagePoliciesQuery({ @@ -220,98 +242,218 @@ export const SettingsPage: React.FC = memo(({ packageInfo, theme$ }: Prop ); return ( - - - - - -

    - -

    -
    - - {installedVersion !== null && ( -
    - -

    - -

    -
    - - - - - - + + + + + +

    + +

    +
    + + {installedVersion !== null && ( +
    + +

    + +

    +
    + +
    + + + + + + + + + + + + + + +
    + + {installedVersion} + +
    + + {latestVersion} + +
    + {shouldShowKeepPoliciesUpToDateSwitch && ( + <> + + + + )} + + {(updateAvailable || isUpgradingPackagePolicies) && ( + <> + + +

    + - - - - {installedVersion} - - - - - +

    + + )} +
    + )} + {!hideInstallOptions && !isUpdating && ( +
    + + {installationStatus === InstallStatus.notInstalled || + installationStatus === InstallStatus.installing ? ( +
    + +

    + +

    +
    + +

    - - - - {latestVersion} - - - - - - {shouldShowKeepPoliciesUpToDateSwitch && ( - <> - - - - )} - - {(updateAvailable || isUpgradingPackagePolicies) && ( - <> - - -

    - -

    - - )} -
    - )} - {!hideInstallOptions && !isUpdating && ( -
    - - {installationStatus === InstallStatus.notInstalled || - installationStatus === InstallStatus.installing ? ( +

    + + +

    + +

    +
    +
    +
    + ) : ( + <> + + + +

    + +

    +
    +
    + + + + +
    + +
    +
    + {packageHasUsages && ( + + + , + }} + /> + + + )} +
    + + + + +

    + +

    +
    +
    + + + + +
    + +
    +
    +
    + + )} +
    + )} + {hideInstallOptions && isViewingOldPackage && !isUpdating && ( +
    +

    @@ -326,138 +468,33 @@ export const SettingsPage: React.FC = memo(({ packageInfo, theme$ }: Prop

    - -

    - - -

    - -

    -
    -
    -

    - ) : ( - <> - - - -

    - -

    -
    -
    - - - - -
    - -
    -
    - {packageHasUsages && ( - - - , - }} - /> - - - )} -
    - - - - -

    - -

    -
    -
    - + , + }} /> - - -
    - -
    -
    -
    - - )} -
    - )} - {hideInstallOptions && isViewingOldPackage && !isUpdating && ( -
    - -
    - -

    - -

    -
    - -

    - - , - }} - /> - -

    + +

    +
    -
    - )} - - - + )} + + + + + {isChangelogModalOpen && ( + + )} + + ); }); diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/utils/changelog_utils.test.ts b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/utils/changelog_utils.test.ts new file mode 100644 index 0000000000000..5a29eba562a75 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/utils/changelog_utils.test.ts @@ -0,0 +1,119 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { filterYamlChangelog } from '.'; + +describe('filterYamlChangelog', () => { + const changelogText = ` +- version: "2.4.0" + changes: + - description: Update package to ECS 8.6.0. + type: enhancement + link: https://github.com/elastic/integrations/pull/4576 +- version: "2.3.0" + changes: + - description: Added support for GCS input. + type: enhancement + link: https://github.com/elastic/integrations/pull/4728 +- version: "2.2.0" + changes: + - description: Update package to ECS 8.5.0. + type: enhancement + link: https://github.com/elastic/integrations/pull/4285 +- version: "2.1.2" + changes: + - description: Remove duplicate fields. + type: bugfix + link: https://github.com/elastic/integrations/pull/4399`; + + it('should return the changelog from latest to current version', () => { + expect(filterYamlChangelog(changelogText, `2.4.0`, `2.2.0`)).toEqual([ + { + version: '2.4.0', + changes: [ + { + description: 'Update package to ECS 8.6.0.', + link: 'https://github.com/elastic/integrations/pull/4576', + type: 'enhancement', + }, + ], + }, + { + version: '2.3.0', + changes: [ + { + description: 'Added support for GCS input.', + link: 'https://github.com/elastic/integrations/pull/4728', + type: 'enhancement', + }, + ], + }, + { + version: '2.2.0', + changes: [ + { + description: 'Update package to ECS 8.5.0.', + link: 'https://github.com/elastic/integrations/pull/4285', + type: 'enhancement', + }, + ], + }, + ]); + }); + + it('should return the changelog to latest version when there is no current version defined', () => { + expect(filterYamlChangelog(changelogText, `2.4.0`)).toEqual([ + { + version: '2.4.0', + changes: [ + { + description: 'Update package to ECS 8.6.0.', + link: 'https://github.com/elastic/integrations/pull/4576', + type: 'enhancement', + }, + ], + }, + { + version: '2.3.0', + changes: [ + { + description: 'Added support for GCS input.', + link: 'https://github.com/elastic/integrations/pull/4728', + type: 'enhancement', + }, + ], + }, + { + version: '2.2.0', + changes: [ + { + description: 'Update package to ECS 8.5.0.', + link: 'https://github.com/elastic/integrations/pull/4285', + type: 'enhancement', + }, + ], + }, + { + version: '2.1.2', + changes: [ + { + description: 'Remove duplicate fields.', + link: 'https://github.com/elastic/integrations/pull/4399', + type: 'bugfix', + }, + ], + }, + ]); + }); + + it('should return empty array if changelog text is undefined', () => { + expect(filterYamlChangelog(undefined, `2.4.0`, `2.2.0`)).toEqual([]); + }); + it('should return empty array if changelog text is null', () => { + expect(filterYamlChangelog(null, `2.4.0`, `2.2.0`)).toEqual([]); + }); +}); diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/utils/changelog_utils.ts b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/utils/changelog_utils.ts new file mode 100644 index 0000000000000..7860704bb208a --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/utils/changelog_utils.ts @@ -0,0 +1,45 @@ +/* + * Copyright 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 { safeLoad } from 'js-yaml'; + +import semverGte from 'semver/functions/gte'; +import semverLte from 'semver/functions/lte'; + +import type { ChangeLogParams } from '../settings/changelog_modal'; + +export const formatChangelog = (parsedChangelog: ChangeLogParams[]) => { + if (!parsedChangelog) return ''; + + return parsedChangelog.reduce((acc, val) => { + acc += `Version: ${val.version}\nChanges:\n Type: ${val.changes[0].type}\n Description: ${val.changes[0].description}\n Link: ${val.changes[0].link}\n\n`; + return acc; + }, ''); +}; + +// Exported for testing +export const filterYamlChangelog = ( + changelogText: string | null | undefined, + latestVersion: string, + currentVersion?: string +) => { + const parsedChangelog: ChangeLogParams[] = changelogText ? safeLoad(changelogText) : []; + + if (!currentVersion) return parsedChangelog.filter((e) => semverLte(e.version, latestVersion)); + + return parsedChangelog.filter( + (e) => semverLte(e.version, latestVersion) && semverGte(e.version, currentVersion) + ); +}; + +export const getFormattedChangelog = ( + changelogText: string | null | undefined, + latestVersion: string, + currentVersion?: string +) => { + const parsed = filterYamlChangelog(changelogText, latestVersion, currentVersion); + return formatChangelog(parsed); +}; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/utils/index.ts b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/utils/index.ts index 020e115f2309a..fa39a2f963396 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/utils/index.ts +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/utils/index.ts @@ -6,3 +6,4 @@ */ export { getInstallPkgRouteOptions } from './get_install_route_options'; +export { getFormattedChangelog, filterYamlChangelog } from './changelog_utils'; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/installed_packages.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/installed_packages.tsx index 695646d20f6a6..04b219875a0c6 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/installed_packages.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/installed_packages.tsx @@ -14,7 +14,12 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { EuiCallOut, EuiLink } from '@elastic/eui'; import { pagePathGetters } from '../../../../constants'; -import { useBreadcrumbs, useLink, useStartServices, useFleetStatus } from '../../../../hooks'; +import { + useBreadcrumbs, + useLink, + useStartServices, + useGetPackageVerificationKeyId, +} from '../../../../hooks'; import { PackageListGrid } from '../../components/package_list_grid'; import type { PackageListItem } from '../../../../types'; @@ -121,7 +126,7 @@ export const InstalledPackages: React.FC<{ }> = ({ installedPackages, isLoading }) => { useBreadcrumbs('integrations_installed'); - const { packageVerificationKeyId } = useFleetStatus(); + const { packageVerificationKeyId } = useGetPackageVerificationKeyId(); const { getHref, getAbsolutePath } = useLink(); diff --git a/x-pack/plugins/fleet/public/hooks/use_fleet_status.tsx b/x-pack/plugins/fleet/public/hooks/use_fleet_status.tsx index 95067a3158d25..d0f3414a4d016 100644 --- a/x-pack/plugins/fleet/public/hooks/use_fleet_status.tsx +++ b/x-pack/plugins/fleet/public/hooks/use_fleet_status.tsx @@ -19,7 +19,6 @@ interface FleetStatusState { error?: Error; missingRequirements?: GetFleetStatusResponse['missing_requirements']; missingOptionalFeatures?: GetFleetStatusResponse['missing_optional_features']; - packageVerificationKeyId?: GetFleetStatusResponse['package_verification_key_id']; } interface FleetStatus extends FleetStatusState { @@ -60,7 +59,6 @@ export const FleetStatusProvider: React.FC = ({ children }) => { isReady: res.data?.isReady ?? false, missingRequirements: res.data?.missing_requirements, missingOptionalFeatures: res.data?.missing_optional_features, - packageVerificationKeyId: res.data?.package_verification_key_id, })); } catch (error) { setState((s) => ({ ...s, isLoading: false, error })); diff --git a/x-pack/plugins/fleet/public/hooks/use_request/agents.ts b/x-pack/plugins/fleet/public/hooks/use_request/agents.ts index ca53345bb5799..4a23950b0bf56 100644 --- a/x-pack/plugins/fleet/public/hooks/use_request/agents.ts +++ b/x-pack/plugins/fleet/public/hooks/use_request/agents.ts @@ -26,8 +26,8 @@ import type { PostBulkAgentUnenrollRequest, PostBulkAgentUnenrollResponse, PostAgentUnenrollResponse, - PutAgentReassignRequest, - PutAgentReassignResponse, + PostAgentReassignRequest, + PostAgentReassignResponse, PostBulkAgentReassignRequest, PostBulkAgentReassignResponse, GetAgentsRequest, @@ -126,13 +126,13 @@ export function sendGetAgentTags(query: GetAgentsRequest['query'], options?: Req }); } -export function sendPutAgentReassign( +export function sendPostAgentReassign( agentId: string, - body: PutAgentReassignRequest['body'], + body: PostAgentReassignRequest['body'], options?: RequestOptions ) { - return sendRequest({ - method: 'put', + return sendRequest({ + method: 'post', path: agentRouteService.getReassignPath(agentId), body, ...options, diff --git a/x-pack/plugins/fleet/public/hooks/use_request/epm.ts b/x-pack/plugins/fleet/public/hooks/use_request/epm.ts index dc3b5d1048e7f..1a50be67099a5 100644 --- a/x-pack/plugins/fleet/public/hooks/use_request/epm.ts +++ b/x-pack/plugins/fleet/public/hooks/use_request/epm.ts @@ -24,6 +24,7 @@ import type { DeletePackageResponse, UpdatePackageRequest, UpdatePackageResponse, + GetVerificationKeyIdResponse, } from '../../types'; import type { FleetErrorResponse, GetStatsResponse } from '../../../common/types'; @@ -142,6 +143,22 @@ export const useGetPackageStats = (pkgName: string) => { }); }; +export const useGetPackageVerificationKeyId = () => { + const { data, ...rest } = useQuery( + ['verification_key_id'], + () => + sendRequestForRq({ + path: epmRouteService.getVerificationKeyIdPath(), + method: 'get', + }) + ); + + return { + packageVerificationKeyId: data?.id || undefined, + ...rest, + }; +}; + export const sendGetPackageInfoByKey = ( pkgName: string, pkgVersion?: string, diff --git a/x-pack/plugins/fleet/public/types/index.ts b/x-pack/plugins/fleet/public/types/index.ts index 1f986e8e27b12..8ce5cc30cde9e 100644 --- a/x-pack/plugins/fleet/public/types/index.ts +++ b/x-pack/plugins/fleet/public/types/index.ts @@ -69,8 +69,8 @@ export type { GetAgentIncomingDataRequest, IncomingDataList, GetAgentIncomingDataResponse, - PutAgentReassignRequest, - PutAgentReassignResponse, + PostAgentReassignRequest, + PostAgentReassignResponse, PostBulkAgentReassignRequest, PostBulkAgentReassignResponse, PostNewAgentActionResponse, @@ -113,6 +113,7 @@ export type { ServiceName, GetCategoriesRequest, GetCategoriesResponse, + GetVerificationKeyIdResponse, GetPackagesRequest, GetPackagesResponse, GetLimitedPackagesResponse, diff --git a/x-pack/plugins/fleet/server/mocks/index.ts b/x-pack/plugins/fleet/server/mocks/index.ts index 4c1d37e3d0f52..8e228f445977e 100644 --- a/x-pack/plugins/fleet/server/mocks/index.ts +++ b/x-pack/plugins/fleet/server/mocks/index.ts @@ -75,12 +75,7 @@ export const createAppContextStartContractMock = ( kibanaBranch: 'main', telemetryEventsSender: createMockTelemetryEventsSender(), bulkActionsResolver: {} as any, - messageSigningService: { - isEncryptionAvailable: true, - generateKeyPair: jest.fn(), - sign: jest.fn(), - getPublicKey: jest.fn(), - }, + messageSigningService: createMessageSigningServiceMock(), }; }; @@ -165,3 +160,12 @@ export const createMockAgentClient = () => agentServiceMock.createClient(); * Creates a mock PackageService */ export const createMockPackageService = () => packageServiceMock.create(); + +export function createMessageSigningServiceMock() { + return { + isEncryptionAvailable: true, + generateKeyPair: jest.fn(), + sign: jest.fn(), + getPublicKey: jest.fn(), + }; +} diff --git a/x-pack/plugins/fleet/server/plugin.ts b/x-pack/plugins/fleet/server/plugin.ts index 265431a87e265..4e9a7f19b1818 100644 --- a/x-pack/plugins/fleet/server/plugin.ts +++ b/x-pack/plugins/fleet/server/plugin.ts @@ -8,7 +8,7 @@ import type { Observable } from 'rxjs'; import { BehaviorSubject } from 'rxjs'; import { take, filter } from 'rxjs/operators'; - +import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common'; import { i18n } from '@kbn/i18n'; import type { CoreSetup, @@ -118,7 +118,7 @@ export interface FleetSetupDeps { encryptedSavedObjects: EncryptedSavedObjectsPluginSetup; cloud?: CloudSetup; usageCollection?: UsageCollectionSetup; - spaces: SpacesPluginStart; + spaces?: SpacesPluginStart; telemetry?: TelemetryPluginSetup; taskManager: TaskManagerSetupContract; } @@ -386,7 +386,7 @@ export class FleetPlugin return getInternalSoClient(); }, get spaceId() { - return deps.spaces.spacesService.getSpaceId(request); + return deps.spaces?.spacesService?.getSpaceId(request) ?? DEFAULT_SPACE_ID; }, get limitedToPackages() { diff --git a/x-pack/plugins/fleet/server/routes/agent/handlers.ts b/x-pack/plugins/fleet/server/routes/agent/handlers.ts index 24dd0d355411d..b001b27d15b27 100644 --- a/x-pack/plugins/fleet/server/routes/agent/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/agent/handlers.ts @@ -29,6 +29,7 @@ import type { GetAvailableVersionsResponse, GetActionStatusResponse, GetAgentUploadsResponse, + PostAgentReassignResponse, } from '../../../common/types'; import type { GetAgentsRequestSchema, @@ -38,7 +39,8 @@ import type { DeleteAgentRequestSchema, GetAgentStatusRequestSchema, GetAgentDataRequestSchema, - PutAgentReassignRequestSchema, + PutAgentReassignRequestSchemaDeprecated, + PostAgentReassignRequestSchema, PostBulkAgentReassignRequestSchema, PostBulkUpdateAgentTagsRequestSchema, GetActionStatusRequestSchema, @@ -234,10 +236,10 @@ export const getAgentTagsHandler: RequestHandler< } }; -export const putAgentsReassignHandler: RequestHandler< - TypeOf, +export const putAgentsReassignHandlerDeprecated: RequestHandler< + TypeOf, undefined, - TypeOf + TypeOf > = async (context, request, response) => { const coreContext = await context.core; const soClient = coreContext.savedObjects.client; @@ -257,6 +259,29 @@ export const putAgentsReassignHandler: RequestHandler< } }; +export const postAgentsReassignHandler: RequestHandler< + TypeOf, + undefined, + TypeOf +> = async (context, request, response) => { + const coreContext = await context.core; + const soClient = coreContext.savedObjects.client; + const esClient = coreContext.elasticsearch.client.asInternalUser; + try { + await AgentService.reassignAgent( + soClient, + esClient, + request.params.agentId, + request.body.policy_id + ); + + const body: PostAgentReassignResponse = {}; + return response.ok({ body }); + } catch (error) { + return defaultFleetErrorHandler({ error, response }); + } +}; + export const postBulkAgentsReassignHandler: RequestHandler< undefined, undefined, diff --git a/x-pack/plugins/fleet/server/routes/agent/index.ts b/x-pack/plugins/fleet/server/routes/agent/index.ts index d7eb3657cbfd6..9e5204e64ac26 100644 --- a/x-pack/plugins/fleet/server/routes/agent/index.ts +++ b/x-pack/plugins/fleet/server/routes/agent/index.ts @@ -21,7 +21,8 @@ import { GetAgentStatusRequestSchema, GetAgentDataRequestSchema, PostNewAgentActionRequestSchema, - PutAgentReassignRequestSchema, + PutAgentReassignRequestSchemaDeprecated, + PostAgentReassignRequestSchema, PostBulkAgentReassignRequestSchema, PostAgentUpgradeRequestSchema, PostBulkAgentUpgradeRequestSchema, @@ -46,7 +47,7 @@ import { updateAgentHandler, deleteAgentHandler, getAgentStatusForAgentPolicyHandler, - putAgentsReassignHandler, + putAgentsReassignHandlerDeprecated, postBulkAgentsReassignHandler, getAgentDataHandler, bulkUpdateAgentTagsHandler, @@ -54,6 +55,7 @@ import { getActionStatusHandler, getAgentUploadsHandler, getAgentUploadFileHandler, + postAgentsReassignHandler, } from './handlers'; import { postNewAgentActionHandlerBuilder, @@ -178,15 +180,27 @@ export const registerAPIRoutes = (router: FleetAuthzRouter, config: FleetConfigT postAgentUnenrollHandler ); + // mark as deprecated router.put( { path: AGENT_API_ROUTES.REASSIGN_PATTERN, - validate: PutAgentReassignRequestSchema, + validate: PutAgentReassignRequestSchemaDeprecated, fleetAuthz: { fleet: { all: true }, }, }, - putAgentsReassignHandler + putAgentsReassignHandlerDeprecated + ); + + router.post( + { + path: AGENT_API_ROUTES.REASSIGN_PATTERN, + validate: PostAgentReassignRequestSchema, + fleetAuthz: { + fleet: { all: true }, + }, + }, + postAgentsReassignHandler ); router.post( diff --git a/x-pack/plugins/fleet/server/routes/epm/handlers.ts b/x-pack/plugins/fleet/server/routes/epm/handlers.ts index d0fef5e241708..8bacf87284271 100644 --- a/x-pack/plugins/fleet/server/routes/epm/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/epm/handlers.ts @@ -24,6 +24,7 @@ import type { IBulkInstallPackageHTTPError, GetStatsResponse, UpdatePackageResponse, + GetVerificationKeyIdResponse, } from '../../../common/types'; import type { GetCategoriesRequestSchema, @@ -58,6 +59,7 @@ import { getArchiveEntry } from '../../services/epm/archive/cache'; import { getAsset } from '../../services/epm/archive/storage'; import { getPackageUsageStats } from '../../services/epm/packages/get'; import { updatePackage } from '../../services/epm/packages/update'; +import { getGpgKeyIdOrUndefined } from '../../services/epm/packages/package_verification'; const CACHE_CONTROL_10_MINUTES_HEADER: HttpResponseOptions['headers'] = { 'cache-control': 'max-age=600', @@ -414,3 +416,19 @@ export const deletePackageHandler: FleetRequestHandler< return defaultFleetErrorHandler({ error, response }); } }; + +export const getVerificationKeyIdHandler: FleetRequestHandler = async ( + context, + request, + response +) => { + try { + const packageVerificationKeyId = await getGpgKeyIdOrUndefined(); + const body: GetVerificationKeyIdResponse = { + id: packageVerificationKeyId || null, + }; + return response.ok({ body }); + } catch (error) { + return defaultFleetErrorHandler({ error, response }); + } +}; diff --git a/x-pack/plugins/fleet/server/routes/epm/index.ts b/x-pack/plugins/fleet/server/routes/epm/index.ts index 950dca7009355..5c2a34cd90551 100644 --- a/x-pack/plugins/fleet/server/routes/epm/index.ts +++ b/x-pack/plugins/fleet/server/routes/epm/index.ts @@ -53,6 +53,7 @@ import { bulkInstallPackagesFromRegistryHandler, getStatsHandler, updatePackageHandler, + getVerificationKeyIdHandler, } from './handlers'; const MAX_FILE_SIZE_BYTES = 104857600; // 100MB @@ -187,6 +188,17 @@ export const registerRoutes = (router: FleetAuthzRouter) => { deletePackageHandler ); + router.get( + { + path: EPM_API_ROUTES.VERIFICATION_KEY_ID, + validate: false, + fleetAuthz: { + integrations: { readPackageInfo: true }, + }, + }, + getVerificationKeyIdHandler + ); + // deprecated since 8.0 router.get( { diff --git a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts index 72ad9b3e5a99c..4ccb8975630de 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts @@ -675,3 +675,45 @@ describe('generateFleetConfig', () => { `); }); }); + +it('should work with proxy with headers and certificate authorities and certificate and key', () => { + const res = generateFleetConfig( + { + host_urls: ['https://test.fr'], + proxy_id: 'proxy-1', + } as any, + [ + { + id: 'proxy-1', + url: 'https://proxy.fr', + certificate_authorities: ['/tmp/ssl/ca.crt'], + proxy_headers: { Authorization: 'xxx' }, + certificate: 'my-cert', + certificate_key: 'my-key', + } as any, + ] + ); + + expect(res).toMatchInlineSnapshot(` + Object { + "hosts": Array [ + "https://test.fr", + ], + "proxy_headers": Object { + "Authorization": "xxx", + }, + "proxy_url": "https://proxy.fr", + "ssl": Object { + "certificate": "my-cert", + "certificate_authorities": Array [ + Array [ + "/tmp/ssl/ca.crt", + ], + ], + "key": "my-key", + "renegotiation": "never", + "verification_mode": "", + }, + } + `); +}); diff --git a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts index 6fd6e9fdf512b..56e4aba6925e7 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts @@ -227,11 +227,19 @@ export function generateFleetConfig( if (fleetServerHostproxy.proxy_headers) { config.proxy_headers = fleetServerHostproxy.proxy_headers; } - if (fleetServerHostproxy.certificate_authorities) { + if ( + fleetServerHostproxy.certificate_authorities || + fleetServerHostproxy.certificate || + fleetServerHostproxy.certificate_key + ) { config.ssl = { - certificate_authorities: [fleetServerHostproxy.certificate_authorities], renegotiation: 'never', verification_mode: '', + ...(fleetServerHostproxy.certificate_authorities && { + certificate_authorities: [fleetServerHostproxy.certificate_authorities], + }), + ...(fleetServerHostproxy.certificate && { certificate: fleetServerHostproxy.certificate }), + ...(fleetServerHostproxy.certificate_key && { key: fleetServerHostproxy.certificate_key }), }; } } diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.test.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.test.ts index 44e0014630d15..2f148a2f10805 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.test.ts @@ -447,6 +447,27 @@ describe('EPM template', () => { expect(mappings).toEqual(keywordWithMultiFieldsMapping); }); + it('tests processing date field with format', () => { + const dateWithFormatYml = ` +- name: dateWithFormat + type: date + date_format: yyyy-MM-dd +`; + + const dateWithMapping = { + properties: { + dateWithFormat: { + type: 'date', + format: 'yyyy-MM-dd', + }, + }, + }; + const fields: Field[] = safeLoad(dateWithFormatYml); + const processedFields = processFields(fields); + const mappings = generateMappings(processedFields); + expect(mappings).toEqual(dateWithMapping); + }); + it('tests processing wildcard field with multi fields', () => { const keywordWithMultiFieldsLiteralYml = ` - name: keywordWithMultiFields diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.ts index ac7afd143666b..dacdfae720ccf 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.ts @@ -322,6 +322,10 @@ function _generateMappings( fieldProps.type = 'alias'; fieldProps.path = field.path; break; + case 'date': + const dateMappings = generateDateMapping(field); + fieldProps = { ...fieldProps, ...dateMappings, type: 'date' }; + break; default: fieldProps.type = type; } @@ -423,6 +427,14 @@ function generateWildcardMapping(field: Field): IndexTemplateMapping { return mapping; } +function generateDateMapping(field: Field): IndexTemplateMapping { + const mapping: IndexTemplateMapping = {}; + if (field.date_format) { + mapping.format = field.date_format; + } + return mapping; +} + /** * Generates the template name out of the given information */ diff --git a/x-pack/plugins/fleet/server/services/epm/fields/field.ts b/x-pack/plugins/fleet/server/services/epm/fields/field.ts index b8af566463896..4bec0604cca4f 100644 --- a/x-pack/plugins/fleet/server/services/epm/fields/field.ts +++ b/x-pack/plugins/fleet/server/services/epm/fields/field.ts @@ -17,6 +17,7 @@ export interface Field { description?: string; value?: string; format?: string; + date_format?: string; fields?: Fields; enabled?: boolean; path?: string; diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.ts index a3d087d828b01..0b5b86407100e 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.ts @@ -45,7 +45,7 @@ import type { PackageVerificationResult, RegistryDataStream, } from '../../../types'; -import { AUTO_UPGRADE_POLICIES_PACKAGES } from '../../../../common/constants'; +import { AUTO_UPGRADE_POLICIES_PACKAGES, DATASET_VAR_NAME } from '../../../../common/constants'; import { FleetError, PackageOutdatedError, PackagePolicyValidationError } from '../../../errors'; import { PACKAGES_SAVED_OBJECT_TYPE, MAX_TIME_COMPLETE_INSTALL } from '../../../constants'; import { dataStreamService, licenseService } from '../..'; @@ -1033,7 +1033,7 @@ export async function installAssetsForInputPackagePolicy(opts: { const paths = await getArchiveFilelist(pkgInfo); if (!paths) throw new Error('No paths found for '); - const datasetName = packagePolicy.inputs[0].streams[0].vars?.['data_stream.dataset']?.value; + const datasetName = packagePolicy.inputs[0].streams[0].vars?.[DATASET_VAR_NAME]?.value; const [dataStream] = getNormalizedDataStreams(pkgInfo, datasetName); const existingDataStreams = await dataStreamService.getMatchingDataStreams(esClient, { type: dataStream.type, diff --git a/x-pack/plugins/fleet/server/services/package_policy.ts b/x-pack/plugins/fleet/server/services/package_policy.ts index 282cb85b2b895..8561b14dd9ded 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.ts @@ -42,6 +42,7 @@ import { FLEET_APM_PACKAGE, outputType, PACKAGES_SAVED_OBJECT_TYPE, + DATASET_VAR_NAME, } from '../../common/constants'; import type { PostDeletePackagePoliciesResponse, @@ -2147,17 +2148,16 @@ export function _validateRestrictedFieldsNotModifiedOrThrow(opts: { ); if ( oldStream && - oldStream?.vars?.['data_stream.dataset'] && - oldStream?.vars['data_stream.dataset']?.value !== - stream?.vars?.['data_stream.dataset']?.value + oldStream?.vars?.[DATASET_VAR_NAME] && + oldStream?.vars[DATASET_VAR_NAME]?.value !== stream?.vars?.[DATASET_VAR_NAME]?.value ) { // seeing this error in dev? Package policy must be called with prepareInputPackagePolicyDataset function first in UI code appContextService .getLogger() .debug( `Rejecting package policy update due to dataset change, old val '${ - oldStream?.vars['data_stream.dataset']?.value - }, new val '${JSON.stringify(stream?.vars?.['data_stream.dataset']?.value)}'` + oldStream?.vars[DATASET_VAR_NAME]?.value + }, new val '${JSON.stringify(stream?.vars?.[DATASET_VAR_NAME]?.value)}'` ); throw new PackagePolicyValidationError( i18n.translate('xpack.fleet.updatePackagePolicy.datasetCannotBeModified', { diff --git a/x-pack/plugins/fleet/server/types/rest_spec/agent.ts b/x-pack/plugins/fleet/server/types/rest_spec/agent.ts index 92b0f098ae19d..598ea0fa67fe1 100644 --- a/x-pack/plugins/fleet/server/types/rest_spec/agent.ts +++ b/x-pack/plugins/fleet/server/types/rest_spec/agent.ts @@ -120,7 +120,16 @@ export const PostBulkAgentUpgradeRequestSchema = { }), }; -export const PutAgentReassignRequestSchema = { +export const PutAgentReassignRequestSchemaDeprecated = { + params: schema.object({ + agentId: schema.string(), + }), + body: schema.object({ + policy_id: schema.string(), + }), +}; + +export const PostAgentReassignRequestSchema = { params: schema.object({ agentId: schema.string(), }), diff --git a/x-pack/plugins/infra/kibana.jsonc b/x-pack/plugins/infra/kibana.jsonc index 88b8384c7eb18..e17d5bac5a98f 100644 --- a/x-pack/plugins/infra/kibana.jsonc +++ b/x-pack/plugins/infra/kibana.jsonc @@ -24,7 +24,6 @@ "ruleRegistry", "security", "share", - "spaces", "triggersActionsUi", "unifiedSearch", "usageCollection", diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/custom_equation_editor.stories.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/custom_equation_editor.stories.tsx index ce30172a74f15..d2fd0639859a6 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/custom_equation_editor.stories.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/custom_equation_editor.stories.tsx @@ -34,6 +34,24 @@ export default { }, } as Meta; +const fakeDataView = { + title: 'metricbeat-*', + fields: [ + { + name: 'system.cpu.user.pct', + type: 'number', + }, + { + name: 'system.cpu.system.pct', + type: 'number', + }, + { + name: 'system.cpu.cores', + type: 'number', + }, + ], +}; + const CustomEquationEditorTemplate: Story = (args) => { const [expression, setExpression] = useState(args.expression); const [errors, setErrors] = useState(args.errors); @@ -60,6 +78,7 @@ const CustomEquationEditorTemplate: Story = (args) => errors={errors} expression={expression} onChange={handleExpressionChange} + dataView={fakeDataView} /> ); }; diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/custom_equation_editor.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/custom_equation_editor.tsx index 866e818688533..8632616866a7e 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/custom_equation_editor.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/custom_equation_editor.tsx @@ -16,6 +16,7 @@ import React, { useState, useCallback, useMemo } from 'react'; import { omit, range, first, xor, debounce } from 'lodash'; import { IErrorObject } from '@kbn/triggers-actions-ui-plugin/public'; import { FormattedMessage } from '@kbn/i18n-react'; +import { DataViewBase } from '@kbn/es-query'; import { OMITTED_AGGREGATIONS_FOR_CUSTOM_METRICS } from '../../../../../common/http_api'; import { Aggregators, @@ -39,6 +40,7 @@ export interface CustomEquationEditorProps { fields: NormalizedFields; aggregationTypes: AggregationTypes; errors: IErrorObject; + dataView: DataViewBase; } const NEW_METRIC = { name: 'A', aggType: Aggregators.AVERAGE as CustomMetricAggTypes }; @@ -53,6 +55,7 @@ export const CustomEquationEditor = ({ fields, aggregationTypes, errors, + dataView, }: CustomEquationEditorProps) => { const [customMetrics, setCustomMetrics] = useState( expression?.customMetrics ?? [NEW_METRIC] @@ -130,6 +133,7 @@ export const CustomEquationEditor = ({ disableDelete={disableDelete} onChange={handleChange} errors={errors} + dataView={dataView} /> ); } diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/metric_row_with_count.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/metric_row_with_count.tsx index 43ac682830bcd..78bc2bf265b4e 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/metric_row_with_count.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/custom_equation/metric_row_with_count.tsx @@ -4,16 +4,11 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { - EuiFieldText, - EuiFormRow, - EuiHorizontalRule, - EuiFlexItem, - EuiFlexGroup, - EuiSelect, -} from '@elastic/eui'; +import { EuiFormRow, EuiHorizontalRule, EuiFlexItem, EuiFlexGroup, EuiSelect } from '@elastic/eui'; import React, { useCallback, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; +import { DataViewBase } from '@kbn/es-query'; +import { MetricsExplorerKueryBar } from '../../../../pages/metrics/metrics_explorer/components/kuery_bar'; import { Aggregators, CustomMetricAggTypes } from '../../../../../common/alerting/metrics'; import { MetricRowControls } from './metric_row_controls'; import { MetricRowBaseProps } from './types'; @@ -21,6 +16,7 @@ import { MetricRowBaseProps } from './types'; interface MetricRowWithCountProps extends MetricRowBaseProps { agg?: Aggregators; filter?: string; + dataView: DataViewBase; } export const MetricRowWithCount = ({ @@ -31,6 +27,7 @@ export const MetricRowWithCount = ({ disableDelete, onChange, aggregationTypes, + dataView, }: MetricRowWithCountProps) => { const aggOptions = useMemo( () => @@ -59,10 +56,10 @@ export const MetricRowWithCount = ({ ); const handleFilterChange = useCallback( - (el: React.ChangeEvent) => { + (filterString: string) => { onChange({ name, - filter: el.target.value, + filter: filterString, aggType: agg as CustomMetricAggTypes, }); }, @@ -89,7 +86,14 @@ export const MetricRowWithCount = ({ { defaultMessage: 'KQL Filter {name}', values: { name } } )} > - + diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx index 5bc6cff8545e1..224bf8d1fb5d0 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx @@ -311,6 +311,7 @@ export const Expressions: React.FC = (props) => { setRuleParams={updateParams} errors={(errors[idx] as IErrorObject) || emptyError} expression={e || {}} + dataView={derivedIndexPattern} > { timeWindowSize: [], }} expression={expression} + dataView={{ fields: [], title: 'metricbeat-*' }} /> ); diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx index 14ff5b1e60eed..a353b954386c1 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx @@ -27,6 +27,7 @@ import { ThresholdExpression, WhenExpression, } from '@kbn/triggers-actions-ui-plugin/public'; +import { DataViewBase } from '@kbn/es-query'; import { Aggregators, Comparator } from '../../../../common/alerting/metrics'; import { decimalToPct, pctToDecimal } from '../../../../common/utils/corrected_percent_convert'; import { DerivedIndexPattern } from '../../../containers/metrics_source'; @@ -54,6 +55,7 @@ interface ExpressionRowProps { addExpression(): void; remove(id: number): void; setRuleParams(id: number, params: MetricExpression): void; + dataView: DataViewBase; } const StyledExpressionRow = euiStyled(EuiFlexGroup)` @@ -74,8 +76,17 @@ const StyledHealth = euiStyled(EuiHealth)` export const ExpressionRow: React.FC = (props) => { const [isExpanded, setRowState] = useState(true); const toggleRowState = useCallback(() => setRowState(!isExpanded), [isExpanded]); - const { children, setRuleParams, expression, errors, expressionId, remove, fields, canDelete } = - props; + const { + dataView, + children, + setRuleParams, + expression, + errors, + expressionId, + remove, + fields, + canDelete, + } = props; const { aggType = AGGREGATION_TYPES.MAX, @@ -325,6 +336,7 @@ export const ExpressionRow: React.FC = (props) => { aggregationTypes={aggregationType} onChange={handleCustomMetricChange} errors={errors} + dataView={dataView} /> diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/validation.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/validation.tsx index b3d4d423c58b5..83d3919ef6c01 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/validation.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/validation.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import { fromKueryExpression } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; import { ValidationResult } from '@kbn/triggers-actions-ui-plugin/public'; import { isEmpty } from 'lodash'; @@ -48,7 +49,7 @@ export function validateMetricThreshold({ }; metric: string[]; customMetricsError?: string; - customMetrics: Record; + customMetrics: Record; equation?: string; }; } & { filterQuery?: string[] } = {}; @@ -169,7 +170,7 @@ export function validateMetricThreshold({ ); } else { c.customMetrics.forEach((metric) => { - const customMetricErrors: { aggType?: string; field?: string } = {}; + const customMetricErrors: { aggType?: string; field?: string; filter?: string } = {}; if (!metric.aggType) { customMetricErrors.aggType = i18n.translate( 'xpack.infra.metrics.alertFlyout.error.customMetrics.aggTypeRequired', @@ -186,6 +187,13 @@ export function validateMetricThreshold({ } ); } + if (metric.aggType === 'count' && metric.filter) { + try { + fromKueryExpression(metric.filter); + } catch (e) { + customMetricErrors.filter = e.message; + } + } if (!isEmpty(customMetricErrors)) { errors[id].customMetrics[metric.name] = customMetricErrors; } diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/hosts/rx.ts b/x-pack/plugins/infra/public/common/visualizations/lens/hosts/rx.ts index 228d479f39ee2..4b2ee6d8a2eda 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/hosts/rx.ts +++ b/x-pack/plugins/infra/public/common/visualizations/lens/hosts/rx.ts @@ -53,8 +53,8 @@ export class RX implements ILensVisualization { const dataLayer = this.formula.insertOrReplaceFormulaColumn( 'y_network_in_bytes', { - formula: "counter_rate(max(system.network.in.bytes), kql='system.network.in.bytes: *') * 8", - timeScale: 's', + formula: + "average(host.network.ingress.bytes) * 8 / (max(metricset.period, kql='host.network.ingress.bytes: *') / 1000)", format: { id: 'bits', params: { @@ -95,13 +95,13 @@ export class RX implements ILensVisualization { negate: false, alias: null, index: '3be1e71b-4bc5-4462-a314-04539f877a19', - key: 'system.network.in.bytes', + key: 'host.network.ingress.bytes', value: 'exists', type: 'exists', }, query: { exists: { - field: 'system.network.in.bytes', + field: 'host.network.ingress.bytes', }, }, $state: { diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/hosts/tx.ts b/x-pack/plugins/infra/public/common/visualizations/lens/hosts/tx.ts index da5e4635d55ca..236efed938046 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/hosts/tx.ts +++ b/x-pack/plugins/infra/public/common/visualizations/lens/hosts/tx.ts @@ -54,8 +54,7 @@ export class TX implements ILensVisualization { 'y_network_out_bytes', { formula: - "counter_rate(max(system.network.out.bytes), kql='system.network.out.bytes: *') * 8", - timeScale: 's', + "average(host.network.egress.bytes) * 8 / (max(metricset.period, kql='host.network.egress.bytes: *') / 1000)", format: { id: 'bits', params: { @@ -96,13 +95,13 @@ export class TX implements ILensVisualization { negate: false, alias: null, index: '3be1e71b-4bc5-4462-a314-04539f877a19', - key: 'system.network.out.bytes', + key: 'host.network.egress.bytes', value: 'exists', type: 'exists', }, query: { exists: { - field: 'system.network.out.bytes', + field: 'host.network.egress.bytes', }, }, $state: { diff --git a/x-pack/plugins/infra/public/components/autocomplete_field/autocomplete_field.tsx b/x-pack/plugins/infra/public/components/autocomplete_field/autocomplete_field.tsx index e264da646949c..657db92072275 100644 --- a/x-pack/plugins/infra/public/components/autocomplete_field/autocomplete_field.tsx +++ b/x-pack/plugins/infra/public/components/autocomplete_field/autocomplete_field.tsx @@ -24,6 +24,7 @@ interface AutocompleteFieldProps { disabled?: boolean; autoFocus?: boolean; 'aria-label'?: string; + compressed?: boolean; } interface AutocompleteFieldState { @@ -53,6 +54,7 @@ export class AutocompleteField extends React.Component< value, disabled, 'aria-label': ariaLabel, + compressed, } = this.props; const { areSuggestionsVisible, selectedIndex } = this.state; @@ -60,6 +62,7 @@ export class AutocompleteField extends React.Component< (initializer: () => Type) => { + const ref = useRef(null); + if (ref.current === null) { + ref.current = initializer(); + } + return ref as MutableRefObject; +}; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/controls_content.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/controls_content.tsx index 064c50472d25f..79e95f7482b04 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/controls_content.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/controls_content.tsx @@ -60,18 +60,20 @@ export const ControlsContent: React.FC = ({ return ( ({ - id: dataView.id ?? '', - type: CONTROL_GROUP_TYPE, - timeRange, - refreshConfig: REFRESH_CONFIG, - viewMode: ViewMode.VIEW, - filters: [...filters], - query, - chainingSystem: 'HIERARCHICAL', - controlStyle: 'oneLine', - defaultControlWidth: 'small', - panels: controlPanel, + getCreationOptions={async () => ({ + initialInput: { + id: dataView.id ?? '', + type: CONTROL_GROUP_TYPE, + timeRange, + refreshConfig: REFRESH_CONFIG, + viewMode: ViewMode.VIEW, + filters: [...filters], + query, + chainingSystem: 'HIERARCHICAL', + controlStyle: 'oneLine', + defaultControlWidth: 'small', + panels: controlPanel, + }, })} onLoadComplete={(newControlGroup) => { setControlGroup(newControlGroup); diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/alerts/alerts_tab_content.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/alerts/alerts_tab_content.tsx index 1974782c0ba42..cb31f2c5e9102 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/alerts/alerts_tab_content.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/alerts/alerts_tab_content.tsx @@ -27,35 +27,26 @@ import { DEFAULT_INTERVAL, infraAlertFeatureIds, } from '../config'; -import { useAlertsQuery } from '../../../hooks/use_alerts_query'; +import { AlertsEsQuery, useAlertsQuery } from '../../../hooks/use_alerts_query'; import AlertsStatusFilter from './alerts_status_filter'; +import { HostsState } from '../../../hooks/use_unified_search_url_state'; -export const AlertsTabContent = React.memo(() => { +export const AlertsTabContent = () => { const { services } = useKibana(); const { alertStatus, setAlertStatus, alertsEsQueryByStatus } = useAlertsQuery(); const { unifiedSearchDateRange } = useUnifiedSearchContext(); - const summaryTimeRange = useSummaryTimeRange(unifiedSearchDateRange); + const { application, cases, triggersActionsUi } = services; - const { application, cases, charts, triggersActionsUi } = services; - - const { - alertsTableConfigurationRegistry, - getAlertsStateTable: AlertsStateTable, - getAlertSummaryWidget: AlertSummaryWidget, - } = triggersActionsUi; + const { alertsTableConfigurationRegistry, getAlertsStateTable: AlertsStateTable } = + triggersActionsUi; const CasesContext = cases.ui.getCasesContext(); const uiCapabilities = application?.capabilities; const casesCapabilities = cases.helpers.getUICapabilities(uiCapabilities.observabilityCases); - const chartThemes = { - theme: charts.theme.useChartsTheme(), - baseTheme: charts.theme.useChartsBaseTheme(), - }; - return ( @@ -65,12 +56,9 @@ export const AlertsTabContent = React.memo(() => { - {alertsEsQueryByStatus && ( @@ -97,7 +85,38 @@ export const AlertsTabContent = React.memo(() => { ); -}); +}; + +interface MemoAlertSummaryWidgetProps { + alertsQuery: AlertsEsQuery; + dateRange: HostsState['dateRange']; +} + +const MemoAlertSummaryWidget = React.memo( + ({ alertsQuery, dateRange }: MemoAlertSummaryWidgetProps) => { + const { services } = useKibana(); + + const summaryTimeRange = useSummaryTimeRange(dateRange); + + const { charts, triggersActionsUi } = services; + const { getAlertSummaryWidget: AlertSummaryWidget } = triggersActionsUi; + + const chartThemes = { + theme: charts.theme.useChartsTheme(), + baseTheme: charts.theme.useChartsBaseTheme(), + }; + + return ( + + ); + } +); const useSummaryTimeRange = (unifiedSearchDateRange: TimeRange) => { const timeBuckets = useTimeBuckets(); diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/alerts_tab_badge.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/alerts_tab_badge.tsx index 2e28e358e29ab..534d28e671fe3 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/alerts_tab_badge.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/alerts_tab_badge.tsx @@ -36,9 +36,12 @@ export const AlertsTabBadge = () => { ); } - return ( + const shouldRenderBadge = + typeof alertsCount?.activeAlertCount === 'number' && alertsCount.activeAlertCount > 0; + + return shouldRenderBadge ? ( {alertsCount?.activeAlertCount} - ); + ) : null; }; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/tabs.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/tabs.tsx index 7b999175e79fa..4abe6d4498900 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/tabs.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/tabs.tsx @@ -5,14 +5,15 @@ * 2.0. */ -import React, { useRef, useState } from 'react'; +import React from 'react'; import { EuiTabs, EuiTab, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { useLazyRef } from '../../../../../hooks/use_lazy_ref'; import { MetricsGrid } from './metrics/metrics_grid'; import { AlertsTabContent } from './alerts'; import { AlertsTabBadge } from './alerts_tab_badge'; -import { TabIds } from '../../types'; +import { TabIds, useTabId } from '../../hooks/use_tab_id'; const tabs = [ { @@ -32,14 +33,11 @@ const tabs = [ }, ]; -const initialRenderedTabsSet = new Set([tabs[0].id]); - export const Tabs = () => { + const [selectedTabId, setSelectedTabId] = useTabId(tabs[0].id); // This map allow to keep track of which tabs content have been rendered the first time. // We need it in order to load a tab content only if it gets clicked, and then keep it in the DOM for performance improvement. - const renderedTabsSet = useRef(initialRenderedTabsSet); - - const [selectedTabId, setSelectedTabId] = useState(tabs[0].id); + const renderedTabsSet = useLazyRef(() => new Set([selectedTabId])); const tabEntries = tabs.map((tab, index) => ( { const { hostNodes } = useHostsView(); @@ -60,7 +64,7 @@ const createAlertsEsQuery = ({ dateRange: HostsState['dateRange']; hostNodes: SnapshotNode[]; status?: AlertStatus; -}) => { +}): AlertsEsQuery => { const alertStatusQuery = createAlertStatusQuery(status); const dateFilter = createDateFilter(dateRange); diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx index 558a7bac920b7..2db824f9c01d7 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx @@ -163,17 +163,17 @@ export const useHostsTable = (nodes: SnapshotNode[], { time }: HostTableParams) align: 'right', }, { - name: averageTXLabel, - field: 'tx.avg', + name: averageRXLabel, + field: 'rx.avg', sortable: true, - render: (avg: number) => formatMetric('tx', avg), + render: (avg: number) => formatMetric('rx', avg), align: 'right', }, { - name: averageRXLabel, - field: 'rx.avg', + name: averageTXLabel, + field: 'tx.avg', sortable: true, - render: (avg: number) => formatMetric('rx', avg), + render: (avg: number) => formatMetric('tx', avg), align: 'right', }, { diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_tab_id.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_tab_id.ts new file mode 100644 index 0000000000000..2aa5e0493a8e6 --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_tab_id.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as rt from 'io-ts'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { fold } from 'fp-ts/lib/Either'; +import { constant, identity } from 'fp-ts/lib/function'; +import { useUrlState } from '../../../../utils/use_url_state'; + +const TAB_ID_URL_STATE_KEY = 'tabId'; + +export const useTabId = (initialValue: TabId = TabIds.METRICS): [TabId, TabIdUpdater] => { + return useUrlState({ + defaultState: initialValue, + decodeUrlState: makeDecodeUrlState(initialValue), + encodeUrlState, + urlStateKey: TAB_ID_URL_STATE_KEY, + }); +}; + +const TabIdRT = rt.union([rt.literal('alerts'), rt.literal('metrics')]); + +export enum TabIds { + ALERTS = 'alerts', + METRICS = 'metrics', +} + +type TabId = rt.TypeOf; +type TabIdUpdater = (tabId: TabId) => void; + +const encodeUrlState = TabIdRT.encode; +const makeDecodeUrlState = (initialValue: TabId) => (value: unknown) => { + return pipe(TabIdRT.decode(value), fold(constant(initialValue), identity)); +}; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/types.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/types.ts index 08cd6ea3a56bf..b6b1407a0f5b6 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/types.ts +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/types.ts @@ -8,11 +8,6 @@ import { ALERT_STATUS_ACTIVE, ALERT_STATUS_RECOVERED } from '@kbn/rule-data-utils'; import { ALERT_STATUS_ALL } from './constants'; -export enum TabIds { - ALERTS = 'alerts', - METRICS = 'metrics', -} - export type AlertStatus = | typeof ALERT_STATUS_ACTIVE | typeof ALERT_STATUS_RECOVERED diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/kuery_bar.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/kuery_bar.tsx index 0e7d4e6d41e1b..ef2ca1e842ed4 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/kuery_bar.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/kuery_bar.tsx @@ -28,6 +28,7 @@ interface Props { value?: string | null; placeholder?: string; curryLoadSuggestions?: CurryLoadSuggestionsType; + compressed?: boolean; } function validateQuery(query: string) { @@ -46,6 +47,7 @@ export const MetricsExplorerKueryBar = ({ value, placeholder, curryLoadSuggestions = defaultCurryLoadSuggestions, + compressed, }: Props) => { const [draftQuery, setDraftQuery] = useState(value || ''); const [isValid, setValidation] = useState(true); @@ -81,6 +83,7 @@ export const MetricsExplorerKueryBar = ({ {({ isLoadingSuggestions, loadSuggestions, suggestions }) => ( { + it('flattens multi level item', () => { + const data = { + key1: { + item1: 'value 1', + item2: { itemA: 'value 2' }, + }, + key2: { + item3: { itemA: { itemAB: 'value AB' } }, + item4: 'value 4', + }, + }; + + const flatten = flattenObject(data); + expect(flatten).toEqual({ + 'key2.item3.itemA.itemAB': 'value AB', + 'key2.item4': 'value 4', + 'key1.item1': 'value 1', + 'key1.item2.itemA': 'value 2', + }); + }); + + it('does not flatten an array item', () => { + const data = { + key1: { + item1: 'value 1', + item2: { itemA: 'value 2' }, + }, + key2: { + item3: { itemA: { itemAB: 'value AB' } }, + item4: 'value 4', + item5: [1], + item6: { itemA: [1, 2, 3] }, + }, + key3: ['item7', 'item8'], + }; + + const flatten = flattenObject(data); + expect(flatten).toEqual({ + key3: ['item7', 'item8'], + 'key2.item3.itemA.itemAB': 'value AB', + 'key2.item4': 'value 4', + 'key2.item5': [1], + 'key2.item6.itemA': [1, 2, 3], + 'key1.item1': 'value 1', + 'key1.item2.itemA': 'value 2', + }); + }); +}); diff --git a/x-pack/plugins/infra/server/lib/alerting/common/utils.ts b/x-pack/plugins/infra/server/lib/alerting/common/utils.ts index d59192306874a..01c69d4f5d126 100644 --- a/x-pack/plugins/infra/server/lib/alerting/common/utils.ts +++ b/x-pack/plugins/infra/server/lib/alerting/common/utils.ts @@ -226,18 +226,7 @@ export const shouldTermsAggOnContainer = (groupBy: string | string[] | undefined export const flattenAdditionalContext = ( additionalContext: AdditionalContext | undefined | null ): AdditionalContext => { - let flattenedContext: AdditionalContext = {}; - if (additionalContext) { - Object.keys(additionalContext).forEach((context: string) => { - if (additionalContext[context]) { - flattenedContext = { - ...flattenedContext, - ...flattenObject(additionalContext[context], [context + '.']), - }; - } - }); - } - return flattenedContext; + return additionalContext ? flattenObject(additionalContext) : {}; }; export const getContextForRecoveredAlerts = ( @@ -261,39 +250,24 @@ export const unflattenObject = (object: ob return acc; }, {} as T); -/** - * Wrap the key with [] if it is a key from an Array - * @param key The object key - * @param isArrayItem Flag to indicate if it is the key of an Array - */ -const renderKey = (key: string, isArrayItem: boolean): string => (isArrayItem ? `[${key}]` : key); - -export const flattenObject = ( - obj: AdditionalContext, - prefix: string[] = [], - isArrayItem = false -): AdditionalContext => - Object.keys(obj).reduce((acc, k) => { - const nextValue = obj[k]; - - if (typeof nextValue === 'object' && nextValue !== null) { - const isNextValueArray = Array.isArray(nextValue); - const dotSuffix = isNextValueArray ? '' : '.'; - - if (Object.keys(nextValue).length > 0) { - return { - ...acc, - ...flattenObject( - nextValue, - [...prefix, `${renderKey(k, isArrayItem)}${dotSuffix}`], - isNextValueArray - ), - }; +export const flattenObject = (obj: AdditionalContext, prefix: string = ''): AdditionalContext => + Object.keys(obj).reduce((acc, key) => { + const nextValue = obj[key]; + + if (nextValue) { + if (typeof nextValue === 'object' && !Array.isArray(nextValue)) { + const dotSuffix = '.'; + if (Object.keys(nextValue).length > 0) { + return { + ...acc, + ...flattenObject(nextValue, `${prefix}${key}${dotSuffix}`), + }; + } } - } - const fullPath = `${prefix.join('')}${renderKey(k, isArrayItem)}`; - acc[fullPath] = nextValue; + const fullPath = `${prefix}${key}`; + acc[fullPath] = nextValue; + } return acc; }, {}); diff --git a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.test.ts b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.test.ts new file mode 100644 index 0000000000000..63b0c81108a47 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.test.ts @@ -0,0 +1,301 @@ +/* + * Copyright 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 { + AlertInstanceContext as AlertContext, + AlertInstanceState as AlertState, +} from '@kbn/alerting-plugin/server'; +import { + AlertInstanceMock, + RuleExecutorServicesMock, + alertsMock, +} from '@kbn/alerting-plugin/server/mocks'; +import { LifecycleAlertServices } from '@kbn/rule-registry-plugin/server'; +import { ruleRegistryMocks } from '@kbn/rule-registry-plugin/server/mocks'; +import { createLifecycleRuleExecutorMock } from '@kbn/rule-registry-plugin/server/utils/create_lifecycle_rule_executor_mock'; +import { + Aggregators, + Comparator, + InventoryMetricConditions, +} from '../../../../common/alerting/metrics'; + +import type { LogMeta, Logger } from '@kbn/logging'; +import { DEFAULT_FLAPPING_SETTINGS } from '@kbn/alerting-plugin/common'; +import { createInventoryMetricThresholdExecutor } from './inventory_metric_threshold_executor'; +import { ConditionResult } from './evaluate_condition'; +import { InfraBackendLibs } from '../../infra_types'; +import { infraPluginMock } from '../../../mocks'; + +jest.mock('./evaluate_condition', () => ({ evaluateCondition: jest.fn() })); + +interface AlertTestInstance { + instance: AlertInstanceMock; + actionQueue: any[]; + state: any; +} + +const persistAlertInstances = false; + +const fakeLogger = (msg: string, meta?: Meta) => {}; + +const logger = { + trace: fakeLogger, + debug: fakeLogger, + info: fakeLogger, + warn: fakeLogger, + error: fakeLogger, + fatal: fakeLogger, + log: () => void 0, + get: () => logger, +} as unknown as Logger; + +const mockOptions = { + executionId: '', + startedAt: new Date(), + previousStartedAt: null, + spaceId: '', + rule: { + id: '', + name: '', + tags: [], + consumer: '', + enabled: true, + schedule: { + interval: '1h', + }, + actions: [], + createdBy: null, + updatedBy: null, + createdAt: new Date(), + updatedAt: new Date(), + throttle: null, + notifyWhen: null, + producer: '', + ruleTypeId: '', + ruleTypeName: '', + muteAll: false, + }, + logger, + flappingSettings: DEFAULT_FLAPPING_SETTINGS, +}; + +const setEvaluationResults = (response: Record) => { + jest.requireMock('./evaluate_condition').evaluateCondition.mockImplementation(() => response); +}; +const createMockStaticConfiguration = (sources: any) => ({ + alerting: { + inventory_threshold: { + group_by_page_size: 100, + }, + metric_threshold: { + group_by_page_size: 100, + }, + }, + inventory: { + compositeSize: 2000, + }, + sources, +}); + +const mockLibs = { + sources: { + getSourceConfiguration: (savedObjectsClient: any, sourceId: string) => { + return Promise.resolve({ + id: sourceId, + configuration: { + logIndices: { + type: 'index_pattern', + indexPatternId: 'some-id', + }, + }, + }); + }, + }, + getStartServices: () => [ + null, + infraPluginMock.createSetupContract(), + infraPluginMock.createStartContract(), + ], + configuration: createMockStaticConfiguration({}), + metricsRules: { + createLifecycleRuleExecutor: createLifecycleRuleExecutorMock, + }, + basePath: { + publicBaseUrl: 'http://localhost:5601', + prepend: (path: string) => path, + }, + logger, +} as unknown as InfraBackendLibs; + +const alertsServices = alertsMock.createRuleExecutorServices(); +const services: RuleExecutorServicesMock & + LifecycleAlertServices = { + ...alertsServices, + ...ruleRegistryMocks.createLifecycleAlertServices(alertsServices), +}; + +const alertInstances = new Map(); + +services.alertFactory.create.mockImplementation((instanceID: string) => { + const newAlertInstance: AlertTestInstance = { + instance: alertsMock.createAlertFactory.create(), + actionQueue: [], + state: {}, + }; + + const alertInstance: AlertTestInstance = persistAlertInstances + ? alertInstances.get(instanceID) || newAlertInstance + : newAlertInstance; + alertInstances.set(instanceID, alertInstance); + + alertInstance.instance.scheduleActions.mockImplementation((id: string, action: any) => { + alertInstance.actionQueue.push({ id, action }); + return alertInstance.instance; + }); + + return alertInstance.instance; +}); + +function mostRecentAction(id: string) { + const instance = alertInstances.get(id); + if (!instance) return undefined; + return instance.actionQueue.pop(); +} + +function clearInstances() { + alertInstances.clear(); +} + +const executor = createInventoryMetricThresholdExecutor(mockLibs); + +const baseCriterion = { + aggType: Aggregators.AVERAGE, + metric: 'count', + timeSize: 1, + timeUnit: 'm', + threshold: [0], + comparator: Comparator.GT, +} as InventoryMetricConditions; + +describe('The inventory threshold alert type', () => { + describe('querying with Hosts and rule tags', () => { + afterAll(() => clearInstances()); + const execute = (comparator: Comparator, threshold: number[], state?: any) => + executor({ + ...mockOptions, + services, + params: { + nodeType: 'host', + criteria: [ + { + ...baseCriterion, + comparator, + threshold, + }, + ], + }, + state: state ?? {}, + rule: { + ...mockOptions.rule, + tags: ['ruleTag1', 'ruleTag2'], + }, + }); + + const instanceIdA = 'host-01'; + const instanceIdB = 'host-02'; + + test('when tags are present in the source, rule tags and source tags are combined in alert context', async () => { + setEvaluationResults({ + 'host-01': { + ...baseCriterion, + metric: 'count', + timeSize: 1, + timeUnit: 'm', + threshold: [0.75], + comparator: Comparator.GT, + shouldFire: true, + shouldWarn: false, + currentValue: 1.0, + isNoData: false, + isError: false, + context: { + tags: ['host-01_tag1', 'host-01_tag2'], + }, + }, + 'host-02': { + ...baseCriterion, + metric: 'count', + timeSize: 1, + timeUnit: 'm', + threshold: [0.75], + comparator: Comparator.GT, + shouldFire: true, + shouldWarn: false, + currentValue: 1.0, + isNoData: false, + isError: false, + context: { + tags: ['host-02_tag1', 'host-02_tag2'], + }, + }, + }); + await execute(Comparator.GT, [0.75]); + expect(mostRecentAction(instanceIdA).action.tags).toStrictEqual([ + 'host-01_tag1', + 'host-01_tag2', + 'ruleTag1', + 'ruleTag2', + ]); + expect(mostRecentAction(instanceIdB).action.tags).toStrictEqual([ + 'host-02_tag1', + 'host-02_tag2', + 'ruleTag1', + 'ruleTag2', + ]); + }); + + test('when tags are NOT present in the source, rule tags are added in alert context', async () => { + setEvaluationResults({ + 'host-01': { + ...baseCriterion, + metric: 'count', + timeSize: 1, + timeUnit: 'm', + threshold: [0.75], + comparator: Comparator.GT, + shouldFire: true, + shouldWarn: false, + currentValue: 1.0, + isNoData: false, + isError: false, + context: { + cloud: undefined, + }, + }, + 'host-02': { + ...baseCriterion, + metric: 'count', + timeSize: 1, + timeUnit: 'm', + threshold: [0.75], + comparator: Comparator.GT, + shouldFire: true, + shouldWarn: false, + currentValue: 1.0, + isNoData: false, + isError: false, + context: { + tags: undefined, + }, + }, + }); + await execute(Comparator.GT, [0.75]); + expect(mostRecentAction(instanceIdA).action.tags).toStrictEqual(['ruleTag1', 'ruleTag2']); + expect(mostRecentAction(instanceIdB).action.tags).toStrictEqual(['ruleTag1', 'ruleTag2']); + }); + }); +}); diff --git a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts index ffd0a7e563339..e62fd9291fc33 100644 --- a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts @@ -76,176 +76,218 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) = InventoryMetricThresholdAlertState, InventoryMetricThresholdAlertContext, InventoryMetricThresholdAllowedActionGroups - >(async ({ services, params, executionId, spaceId, startedAt, rule: { id: ruleId } }) => { - const startTime = Date.now(); + >( + async ({ + services, + params, + executionId, + spaceId, + startedAt, + rule: { id: ruleId, tags: ruleTags }, + }) => { + const startTime = Date.now(); - const { criteria, filterQuery, sourceId = 'default', nodeType, alertOnNoData } = params; + const { criteria, filterQuery, sourceId = 'default', nodeType, alertOnNoData } = params; - if (criteria.length === 0) throw new Error('Cannot execute an alert with 0 conditions'); + if (criteria.length === 0) throw new Error('Cannot execute an alert with 0 conditions'); - const logger = createScopedLogger(libs.logger, 'inventoryRule', { - alertId: ruleId, - executionId, - }); - - const esClient = services.scopedClusterClient.asCurrentUser; - - const { - alertWithLifecycle, - savedObjectsClient, - getAlertStartedDate, - getAlertUuid, - getAlertByAlertUuid, - } = services; - const alertFactory: InventoryMetricThresholdAlertFactory = ( - id, - reason, - actionGroup, - additionalContext - ) => - alertWithLifecycle({ - id, - fields: { - [ALERT_REASON]: reason, - [ALERT_ACTION_GROUP]: actionGroup, - ...flattenAdditionalContext(additionalContext), - }, + const logger = createScopedLogger(libs.logger, 'inventoryRule', { + alertId: ruleId, + executionId, }); - if (!params.filterQuery && params.filterQueryText) { - try { - const { fromKueryExpression } = await import('@kbn/es-query'); - fromKueryExpression(params.filterQueryText); - } catch (e) { - logger.error(e.message); - const actionGroupId = FIRED_ACTIONS.id; // Change this to an Error action group when able - const reason = buildInvalidQueryAlertReason(params.filterQueryText); - const alert = alertFactory(UNGROUPED_FACTORY_KEY, reason, actionGroupId); - const indexedStartedDate = - getAlertStartedDate(UNGROUPED_FACTORY_KEY) ?? startedAt.toISOString(); - const alertUuid = getAlertUuid(UNGROUPED_FACTORY_KEY); - - alert.scheduleActions(actionGroupId, { - alertDetailsUrl: getAlertDetailsUrl(libs.basePath, spaceId, alertUuid), - alertState: stateToAlertMessage[AlertStates.ERROR], - group: UNGROUPED_FACTORY_KEY, - metric: mapToConditionsLookup(criteria, (c) => c.metric), - reason, - timestamp: startedAt.toISOString(), - value: null, - viewInAppUrl: getViewInInventoryAppUrl({ - basePath: libs.basePath, - criteria, - nodeType, - timestamp: indexedStartedDate, - spaceId, - }), + const esClient = services.scopedClusterClient.asCurrentUser; + + const { + alertWithLifecycle, + savedObjectsClient, + getAlertStartedDate, + getAlertUuid, + getAlertByAlertUuid, + } = services; + const alertFactory: InventoryMetricThresholdAlertFactory = ( + id, + reason, + actionGroup, + additionalContext + ) => + alertWithLifecycle({ + id, + fields: { + [ALERT_REASON]: reason, + [ALERT_ACTION_GROUP]: actionGroup, + ...flattenAdditionalContext(additionalContext), + }, }); - return { state: {} }; + if (!params.filterQuery && params.filterQueryText) { + try { + const { fromKueryExpression } = await import('@kbn/es-query'); + fromKueryExpression(params.filterQueryText); + } catch (e) { + logger.error(e.message); + const actionGroupId = FIRED_ACTIONS.id; // Change this to an Error action group when able + const reason = buildInvalidQueryAlertReason(params.filterQueryText); + const alert = alertFactory(UNGROUPED_FACTORY_KEY, reason, actionGroupId); + const indexedStartedDate = + getAlertStartedDate(UNGROUPED_FACTORY_KEY) ?? startedAt.toISOString(); + const alertUuid = getAlertUuid(UNGROUPED_FACTORY_KEY); + + alert.scheduleActions(actionGroupId, { + alertDetailsUrl: getAlertDetailsUrl(libs.basePath, spaceId, alertUuid), + alertState: stateToAlertMessage[AlertStates.ERROR], + group: UNGROUPED_FACTORY_KEY, + metric: mapToConditionsLookup(criteria, (c) => c.metric), + reason, + timestamp: startedAt.toISOString(), + value: null, + viewInAppUrl: getViewInInventoryAppUrl({ + basePath: libs.basePath, + criteria, + nodeType, + timestamp: indexedStartedDate, + spaceId, + }), + }); + + return { state: {} }; + } } - } - const source = await libs.sources.getSourceConfiguration(savedObjectsClient, sourceId); - - const [, , { logViews }] = await libs.getStartServices(); - const logQueryFields: LogQueryFields | undefined = await logViews - .getClient(savedObjectsClient, esClient) - .getResolvedLogView(sourceId) - .then( - ({ indices }) => ({ indexPattern: indices }), - () => undefined + const source = await libs.sources.getSourceConfiguration(savedObjectsClient, sourceId); + + const [, , { logViews }] = await libs.getStartServices(); + const logQueryFields: LogQueryFields | undefined = await logViews + .getClient(savedObjectsClient, esClient) + .getResolvedLogView(sourceId) + .then( + ({ indices }) => ({ indexPattern: indices }), + () => undefined + ); + + const compositeSize = libs.configuration.alerting.inventory_threshold.group_by_page_size; + const results = await Promise.all( + criteria.map((condition) => + evaluateCondition({ + compositeSize, + condition, + esClient, + executionTimestamp: startedAt, + filterQuery, + logger, + logQueryFields, + nodeType, + source, + }) + ) ); - const compositeSize = libs.configuration.alerting.inventory_threshold.group_by_page_size; - const results = await Promise.all( - criteria.map((condition) => - evaluateCondition({ - compositeSize, - condition, - esClient, - executionTimestamp: startedAt, - filterQuery, - logger, - logQueryFields, - nodeType, - source, - }) - ) - ); - - let scheduledActionsCount = 0; - const inventoryItems = Object.keys(first(results)!); - for (const group of inventoryItems) { - // AND logic; all criteria must be across the threshold - const shouldAlertFire = results.every((result) => result[group]?.shouldFire); - const shouldAlertWarn = results.every((result) => result[group]?.shouldWarn); - // AND logic; because we need to evaluate all criteria, if one of them reports no data then the - // whole alert is in a No Data/Error state - const isNoData = results.some((result) => result[group]?.isNoData); - const isError = results.some((result) => result[group]?.isError); - - const nextState = isError - ? AlertStates.ERROR - : isNoData - ? AlertStates.NO_DATA - : shouldAlertFire - ? AlertStates.ALERT - : shouldAlertWarn - ? AlertStates.WARNING - : AlertStates.OK; - let reason; - if (nextState === AlertStates.ALERT || nextState === AlertStates.WARNING) { - reason = results - .map((result) => - buildReasonWithVerboseMetricName( - group, - result[group], - buildFiredAlertReason, - nextState === AlertStates.WARNING - ) - ) - .join('\n'); - } - if (alertOnNoData) { - if (nextState === AlertStates.NO_DATA) { - reason = results - .filter((result) => result[group].isNoData) - .map((result) => - buildReasonWithVerboseMetricName(group, result[group], buildNoDataAlertReason) - ) - .join('\n'); - } else if (nextState === AlertStates.ERROR) { + let scheduledActionsCount = 0; + const inventoryItems = Object.keys(first(results)!); + for (const group of inventoryItems) { + // AND logic; all criteria must be across the threshold + const shouldAlertFire = results.every((result) => result[group]?.shouldFire); + const shouldAlertWarn = results.every((result) => result[group]?.shouldWarn); + // AND logic; because we need to evaluate all criteria, if one of them reports no data then the + // whole alert is in a No Data/Error state + const isNoData = results.some((result) => result[group]?.isNoData); + const isError = results.some((result) => result[group]?.isError); + + const nextState = isError + ? AlertStates.ERROR + : isNoData + ? AlertStates.NO_DATA + : shouldAlertFire + ? AlertStates.ALERT + : shouldAlertWarn + ? AlertStates.WARNING + : AlertStates.OK; + let reason; + if (nextState === AlertStates.ALERT || nextState === AlertStates.WARNING) { reason = results - .filter((result) => result[group].isError) .map((result) => - buildReasonWithVerboseMetricName(group, result[group], buildErrorAlertReason) + buildReasonWithVerboseMetricName( + group, + result[group], + buildFiredAlertReason, + nextState === AlertStates.WARNING + ) ) .join('\n'); } + if (alertOnNoData) { + if (nextState === AlertStates.NO_DATA) { + reason = results + .filter((result) => result[group].isNoData) + .map((result) => + buildReasonWithVerboseMetricName(group, result[group], buildNoDataAlertReason) + ) + .join('\n'); + } else if (nextState === AlertStates.ERROR) { + reason = results + .filter((result) => result[group].isError) + .map((result) => + buildReasonWithVerboseMetricName(group, result[group], buildErrorAlertReason) + ) + .join('\n'); + } + } + if (reason) { + const actionGroupId = + nextState === AlertStates.WARNING ? WARNING_ACTIONS_ID : FIRED_ACTIONS_ID; + + const additionalContext = results && results.length > 0 ? results[0][group].context : {}; + additionalContext.tags = Array.from( + new Set([...(additionalContext.tags ?? []), ...ruleTags]) + ); + + const alert = alertFactory(group, reason, actionGroupId, additionalContext); + const indexedStartedDate = getAlertStartedDate(group) ?? startedAt.toISOString(); + const alertUuid = getAlertUuid(group); + + scheduledActionsCount++; + + const context = { + alertDetailsUrl: getAlertDetailsUrl(libs.basePath, spaceId, alertUuid), + alertState: stateToAlertMessage[nextState], + group, + reason, + metric: mapToConditionsLookup(criteria, (c) => c.metric), + timestamp: startedAt.toISOString(), + threshold: mapToConditionsLookup(criteria, (c) => c.threshold), + value: mapToConditionsLookup(results, (result) => + formatMetric(result[group].metric, result[group].currentValue) + ), + viewInAppUrl: getViewInInventoryAppUrl({ + basePath: libs.basePath, + criteria, + nodeType, + timestamp: indexedStartedDate, + spaceId, + }), + ...additionalContext, + }; + alert.scheduleActions(actionGroupId, context); + } } - if (reason) { - const actionGroupId = - nextState === AlertStates.WARNING ? WARNING_ACTIONS_ID : FIRED_ACTIONS_ID; - const additionalContext = results && results.length > 0 ? results[0][group].context : null; + const { getRecoveredAlerts } = services.alertFactory.done(); + const recoveredAlerts = getRecoveredAlerts(); - const alert = alertFactory(group, reason, actionGroupId, additionalContext); - const indexedStartedDate = getAlertStartedDate(group) ?? startedAt.toISOString(); - const alertUuid = getAlertUuid(group); + for (const alert of recoveredAlerts) { + const recoveredAlertId = alert.getId(); + const indexedStartedDate = getAlertStartedDate(recoveredAlertId) ?? startedAt.toISOString(); + const alertUuid = getAlertUuid(recoveredAlertId); + const alertHits = alertUuid ? await getAlertByAlertUuid(alertUuid) : undefined; + const additionalContext = getContextForRecoveredAlerts(alertHits); + const originalActionGroup = getOriginalActionGroup(alertHits); - scheduledActionsCount++; - - const context = { + alert.setContext({ alertDetailsUrl: getAlertDetailsUrl(libs.basePath, spaceId, alertUuid), - alertState: stateToAlertMessage[nextState], - group, - reason, + alertState: stateToAlertMessage[AlertStates.OK], + group: recoveredAlertId, metric: mapToConditionsLookup(criteria, (c) => c.metric), - timestamp: startedAt.toISOString(), threshold: mapToConditionsLookup(criteria, (c) => c.threshold), - value: mapToConditionsLookup(results, (result) => - formatMetric(result[group].metric, result[group].currentValue) - ), + timestamp: startedAt.toISOString(), viewInAppUrl: getViewInInventoryAppUrl({ basePath: libs.basePath, criteria, @@ -253,49 +295,19 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) = timestamp: indexedStartedDate, spaceId, }), + originalAlertState: translateActionGroupToAlertState(originalActionGroup), + originalAlertStateWasALERT: originalActionGroup === FIRED_ACTIONS_ID, + originalAlertStateWasWARNING: originalActionGroup === WARNING_ACTIONS_ID, ...additionalContext, - }; - alert.scheduleActions(actionGroupId, context); + }); } - } - const { getRecoveredAlerts } = services.alertFactory.done(); - const recoveredAlerts = getRecoveredAlerts(); - - for (const alert of recoveredAlerts) { - const recoveredAlertId = alert.getId(); - const indexedStartedDate = getAlertStartedDate(recoveredAlertId) ?? startedAt.toISOString(); - const alertUuid = getAlertUuid(recoveredAlertId); - const alertHits = alertUuid ? await getAlertByAlertUuid(alertUuid) : undefined; - const additionalContext = getContextForRecoveredAlerts(alertHits); - const originalActionGroup = getOriginalActionGroup(alertHits); - - alert.setContext({ - alertDetailsUrl: getAlertDetailsUrl(libs.basePath, spaceId, alertUuid), - alertState: stateToAlertMessage[AlertStates.OK], - group: recoveredAlertId, - metric: mapToConditionsLookup(criteria, (c) => c.metric), - threshold: mapToConditionsLookup(criteria, (c) => c.threshold), - timestamp: startedAt.toISOString(), - viewInAppUrl: getViewInInventoryAppUrl({ - basePath: libs.basePath, - criteria, - nodeType, - timestamp: indexedStartedDate, - spaceId, - }), - originalAlertState: translateActionGroupToAlertState(originalActionGroup), - originalAlertStateWasALERT: originalActionGroup === FIRED_ACTIONS_ID, - originalAlertStateWasWARNING: originalActionGroup === WARNING_ACTIONS_ID, - ...additionalContext, - }); - } + const stopTime = Date.now(); + logger.debug(`Scheduled ${scheduledActionsCount} actions in ${stopTime - startTime}ms`); - const stopTime = Date.now(); - logger.debug(`Scheduled ${scheduledActionsCount} actions in ${stopTime - startTime}ms`); - - return { state: {} }; - }); + return { state: {} }; + } + ); const formatThreshold = (metric: SnapshotMetricType, value: number | number[]) => { const metricFormatter = get(METRIC_FORMATTERS, metric, METRIC_FORMATTERS.count); diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts index a94521c22bde4..f363b5baeabf1 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts @@ -690,6 +690,143 @@ describe('The metric threshold alert type', () => { }); }); + describe('querying with a groupBy parameter host.name and rule tags', () => { + afterAll(() => clearInstances()); + const execute = ( + comparator: Comparator, + threshold: number[], + groupBy: string[] = ['host.name'], + metric?: string, + state?: any + ) => + executor({ + ...mockOptions, + services, + params: { + groupBy, + criteria: [ + { + ...baseNonCountCriterion, + comparator, + threshold, + metric: metric ?? baseNonCountCriterion.metric, + }, + ], + }, + state: state ?? mockOptions.state.wrapped, + rule: { + ...mockOptions.rule, + tags: ['ruleTag1', 'ruleTag2'], + }, + }); + const instanceIdA = 'host-01'; + const instanceIdB = 'host-02'; + + test('rule tags and source tags are combined in alert context', async () => { + setEvaluationResults([ + { + 'host-01': { + ...baseNonCountCriterion, + comparator: Comparator.GT, + threshold: [0.75], + metric: 'test.metric.1', + currentValue: 1.0, + timestamp: new Date().toISOString(), + shouldFire: true, + shouldWarn: false, + isNoData: false, + bucketKey: { groupBy0: 'host-01' }, + context: { + tags: ['host-01_tag1', 'host-01_tag2'], + }, + }, + 'host-02': { + ...baseNonCountCriterion, + comparator: Comparator.GT, + threshold: [0.75], + metric: 'test.metric.1', + currentValue: 3, + timestamp: new Date().toISOString(), + shouldFire: true, + shouldWarn: false, + isNoData: false, + bucketKey: { groupBy0: 'host-02' }, + context: { + tags: ['host-02_tag1', 'host-02_tag2'], + }, + }, + }, + ]); + await execute(Comparator.GT, [0.75]); + expect(mostRecentAction(instanceIdA).action.tags).toStrictEqual([ + 'host-01_tag1', + 'host-01_tag2', + 'ruleTag1', + 'ruleTag2', + ]); + expect(mostRecentAction(instanceIdB).action.tags).toStrictEqual([ + 'host-02_tag1', + 'host-02_tag2', + 'ruleTag1', + 'ruleTag2', + ]); + }); + }); + + describe('querying without a groupBy parameter and rule tags', () => { + afterAll(() => clearInstances()); + const execute = ( + comparator: Comparator, + threshold: number[], + groupBy: string = '', + metric?: string, + state?: any + ) => + executor({ + ...mockOptions, + services, + params: { + groupBy, + criteria: [ + { + ...baseNonCountCriterion, + comparator, + threshold, + metric: metric ?? baseNonCountCriterion.metric, + }, + ], + }, + state: state ?? mockOptions.state.wrapped, + rule: { + ...mockOptions.rule, + tags: ['ruleTag1', 'ruleTag2'], + }, + }); + + test('rule tags are added in alert context', async () => { + setEvaluationResults([ + { + '*': { + ...baseNonCountCriterion, + comparator: Comparator.GT, + threshold: [0.75], + metric: 'test.metric.1', + currentValue: 1.0, + timestamp: new Date().toISOString(), + shouldFire: true, + shouldWarn: false, + isNoData: false, + bucketKey: { groupBy0: '*' }, + }, + }, + ]); + + const instanceID = '*'; + await execute(Comparator.GT, [0.75]); + expect(mostRecentAction(instanceID).action.tags).toStrictEqual(['ruleTag1', 'ruleTag2']); + }); + }); + describe('querying with multiple criteria', () => { afterAll(() => clearInstances()); const execute = ( diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts index cd0b0f48f36d4..28a32a8c46174 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts @@ -287,9 +287,13 @@ export const createMetricThresholdExecutor = (libs: InfraBackendLibs) => const additionalContext = hasAdditionalContext(params.groupBy, validGroupByForContext) ? alertResults && alertResults.length > 0 - ? alertResults[0][group].context - : null - : null; + ? alertResults[0][group].context ?? {} + : {} + : {}; + + additionalContext.tags = Array.from( + new Set([...(additionalContext.tags ?? []), ...options.rule.tags]) + ); const alert = alertFactory(`${group}`, reason, actionGroupId, additionalContext); const alertUuid = getAlertUuid(group); diff --git a/x-pack/plugins/infra/server/services/log_views/log_views_client.mock.ts b/x-pack/plugins/infra/server/services/log_views/log_views_client.mock.ts index ac69ae4f85ba5..5c20297b82c65 100644 --- a/x-pack/plugins/infra/server/services/log_views/log_views_client.mock.ts +++ b/x-pack/plugins/infra/server/services/log_views/log_views_client.mock.ts @@ -5,11 +5,12 @@ * 2.0. */ +import { createResolvedLogViewMock } from '../../../common/log_views/resolved_log_view.mock'; import { ILogViewsClient } from './types'; export const createLogViewsClientMock = (): jest.Mocked => ({ getLogView: jest.fn(), - getResolvedLogView: jest.fn(), + getResolvedLogView: jest.fn((logViewId: string) => Promise.resolve(createResolvedLogViewMock())), putLogView: jest.fn(), resolveLogView: jest.fn(), }); diff --git a/x-pack/plugins/infra/server/services/rules/rule_data_client.ts b/x-pack/plugins/infra/server/services/rules/rule_data_client.ts index 1435f4812d16c..3a81f957e9314 100644 --- a/x-pack/plugins/infra/server/services/rules/rule_data_client.ts +++ b/x-pack/plugins/infra/server/services/rules/rule_data_client.ts @@ -6,11 +6,11 @@ */ import { CoreSetup, Logger } from '@kbn/core/server'; -import { mappingFromFieldMap } from '@kbn/rule-registry-plugin/common/mapping_from_field_map'; import { experimentalRuleFieldMap } from '@kbn/rule-registry-plugin/common/assets/field_maps/experimental_rule_field_map'; import { Dataset, RuleRegistryPluginSetupContract } from '@kbn/rule-registry-plugin/server'; -import { ECS_COMPONENT_TEMPLATE_NAME } from '@kbn/rule-registry-plugin/common/assets'; +import { mappingFromFieldMap } from '@kbn/alerting-plugin/common'; +import { ECS_COMPONENT_TEMPLATE_NAME } from '@kbn/alerting-plugin/server'; import type { InfraFeatureId } from '../../../common/constants'; import { RuleRegistrationContext, RulesServiceStartDeps } from './types'; diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/formula.test.tsx b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/formula.test.tsx index 72cca338117da..bc711e7d094c1 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/formula.test.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/formula.test.tsx @@ -1520,7 +1520,10 @@ invalid: " operationDefinitionMap ) ).toEqual([ - 'A layer with only static values will not show results, use at least one dynamic metric', + { + message: + 'A layer with only static values will not show results, use at least one dynamic metric', + }, ]); }); @@ -1856,5 +1859,17 @@ invalid: " ).toEqual(undefined); } }); + + it('returns deduped errors on inner operation validation', () => { + expect( + formulaOperation.getErrorMessage!( + getNewLayerWithFormula('sum(clientip) + sum(clientip)', true), + 'col1', + indexPattern, + undefined, + operationDefinitionMap + ) + ).toHaveLength(1); + }); }); }); diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/formula.tsx b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/formula.tsx index 70ccbc5fd25e8..20432dcdb6e24 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/formula.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/formula.tsx @@ -7,7 +7,11 @@ import { i18n } from '@kbn/i18n'; import { uniqBy } from 'lodash'; -import type { BaseIndexPatternColumn, OperationDefinition } from '..'; +import type { + BaseIndexPatternColumn, + FieldBasedOperationErrorMessage, + OperationDefinition, +} from '..'; import type { ReferenceBasedIndexPatternColumn } from '../column_types'; import type { IndexPattern } from '../../../../../types'; import { runASTValidation, tryToParse } from './validation'; @@ -94,22 +98,30 @@ export const formulaOperation: OperationDefinition { - const def = visibleOperationsMap[col.operationType]; - if (def?.getErrorMessage) { - const messages = def.getErrorMessage( - layer, - id, - indexPattern, - dateRange, - visibleOperationsMap - ); - return messages ? { message: messages.join(', ') } : []; - } - return []; - }) - .filter(nonNullable); + const innerErrors = [ + ...managedColumns + .flatMap(([id, col]) => { + const def = visibleOperationsMap[col.operationType]; + if (def?.getErrorMessage) { + // TOOD: it would be nice to have nicer column names here rather than `Part of ` + const messages = def.getErrorMessage( + layer, + id, + indexPattern, + dateRange, + visibleOperationsMap + ); + return messages || []; + } + return []; + }) + .filter(nonNullable) + // dedup messages with the same content + .reduce((memo, message) => { + memo.add(message); + return memo; + }, new Set()), + ]; const hasBuckets = layer.columnOrder.some((colId) => layer.columns[colId].isBucketed); const hasOtherMetrics = layer.columnOrder.some((colId) => { const col = layer.columns[colId]; @@ -135,7 +147,7 @@ export const formulaOperation: OperationDefinition message) : undefined; + return innerErrors.length ? innerErrors : undefined; }, getPossibleOperation() { return { diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/validation.ts b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/validation.ts index a8828a9462ddd..520d0f53b143f 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/validation.ts +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/validation.ts @@ -512,7 +512,7 @@ function checkMissingVariableOrFunctions( }, locations: missingVariables.map(({ location }) => location), }), - extraInfo: { missingFields: missingVariables.map(({ value }) => value) }, + extraInfo: { missingFields: [...new Set(missingVariables.map(({ value }) => value))] }, }); } const invalidVariableErrors = checkVariableEdgeCases( diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/layer_helpers.test.ts b/x-pack/plugins/lens/public/datasources/form_based/operations/layer_helpers.test.ts index ae373cf01d0d0..0490f3c479297 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/layer_helpers.test.ts +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/layer_helpers.test.ts @@ -1299,6 +1299,35 @@ describe('state_helpers', () => { ).toEqual(expect.objectContaining({ label: 'MY CUSTOM LABEL' })); }); + it('should keep the custom label when already in formula and a setting change', () => { + expect( + replaceColumn({ + layer: { + indexPatternId: '1', + columnOrder: ['col1', 'col2'], + columns: { + col1: { + label: 'MY CUSTOM LABEL', + customLabel: true, + dataType: 'number', + operationType: 'formula', + isBucketed: false, + scale: 'ratio', + params: { isFormulaBroken: false, formula: 'average(bytes)' }, + references: [], + } as FormulaIndexPatternColumn, + }, + }, + indexPattern, + columnId: 'col1', + op: 'formula', + field: indexPattern.fields[2], // bytes field + visualizationGroups: [], + shouldResetLabel: undefined, + }).columns.col1 + ).toEqual(expect.objectContaining({ label: 'MY CUSTOM LABEL' })); + }); + it('should not carry over the managed reference default label to the new operation', () => { expect( replaceColumn({ diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/layer_helpers.ts b/x-pack/plugins/lens/public/datasources/form_based/operations/layer_helpers.ts index f4e0a7efebe00..f1b93b33467a5 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/layer_helpers.ts +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/layer_helpers.ts @@ -555,11 +555,7 @@ function replaceFormulaColumn( // when coming to Formula keep the custom label const regeneratedColumn = newLayer.columns[columnId]; - if ( - !shouldResetLabel && - regeneratedColumn.operationType !== previousColumn.operationType && - previousColumn.customLabel - ) { + if (!shouldResetLabel && previousColumn.customLabel) { regeneratedColumn.customLabel = true; regeneratedColumn.label = previousColumn.label; } diff --git a/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts b/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts index 188ddfccea0ba..94438f242ec8d 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts @@ -79,10 +79,10 @@ export const getColumnToLabelMap = ( return columnToLabel; }; -export const getSortedGroups = ( +export const getSortedAccessorsForGroup = ( datasource: DatasourcePublicAPI | undefined, layer: PieLayerState, - accessor: 'primaryGroups' | 'secondaryGroups' = 'primaryGroups' + accessor: 'primaryGroups' | 'secondaryGroups' | 'metrics' ) => { const originalOrder = datasource ?.getTableSpec() @@ -174,7 +174,9 @@ const generateCommonArguments = ( datasourceLayers: DatasourceLayers, paletteService: PaletteRegistry ) => { - const columnToLabelMap = getColumnToLabelMap(layer.metrics, datasourceLayers[layer.layerId]); + const datasource = datasourceLayers[layer.layerId]; + const columnToLabelMap = getColumnToLabelMap(layer.metrics, datasource); + const sortedMetricAccessors = getSortedAccessorsForGroup(datasource, layer, 'metrics'); return { labels: generateCommonLabelsAstArgs(state, attributes, layer, columnToLabelMap), @@ -182,7 +184,7 @@ const generateCommonArguments = ( .filter(({ columnId }) => !isCollapsed(columnId, layer)) .map(({ columnId }) => columnId) .map(prepareDimension), - metrics: (layer.allowMultipleMetrics ? layer.metrics : [layer.metrics[0]]).map( + metrics: (layer.allowMultipleMetrics ? sortedMetricAccessors : [sortedMetricAccessors[0]]).map( prepareDimension ), metricsToLabels: JSON.stringify(columnToLabelMap), @@ -290,16 +292,18 @@ function expressionHelper( const layer = state.layers[0]; const datasource = datasourceLayers[layer.layerId]; - const groups = Array.from( + const accessors = Array.from( new Set( [ - getSortedGroups(datasource, layer, 'primaryGroups'), - layer.secondaryGroups ? getSortedGroups(datasource, layer, 'secondaryGroups') : [], + getSortedAccessorsForGroup(datasource, layer, 'primaryGroups'), + layer.secondaryGroups + ? getSortedAccessorsForGroup(datasource, layer, 'secondaryGroups') + : [], ].flat() ) ); - const operations = groups + const operations = accessors .map((columnId) => ({ columnId, operation: datasource?.getOperationForColumnId(columnId) as Operation | null, @@ -323,11 +327,11 @@ function expressionHelper( type: 'expression', chain: [ ...(datasourceAst ? datasourceAst.chain : []), - ...groups + ...accessors .filter((columnId) => layer.collapseFns?.[columnId]) .map((columnId) => { return buildExpressionFunction('lens_collapse', { - by: groups.filter((chk) => chk !== columnId), + by: accessors.filter((chk) => chk !== columnId), metric: layer.metrics, fn: [layer.collapseFns![columnId]!], }).toAst(); diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts index 2bf830c0028cd..a11c1667f807f 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts @@ -448,119 +448,173 @@ describe('pie_visualization', () => { }); }); - it("doesn't count collapsed columns toward the dimension limits", () => { - const colIds = new Array(PartitionChartsMeta.pie.maxBuckets) - .fill(undefined) - .map((_, i) => String(i + 1)); - - const frame = mockFrame(); - frame.datasourceLayers[LAYER_ID]!.getTableSpec = () => - colIds.map((id) => ({ columnId: id, fields: [] })); + it('orders metric accessors by datasource column order', () => { + const colIds = ['1', '2', '3', '4']; const state = getExampleState(); - state.layers[0].primaryGroups = colIds; - - const getConfig = (_state: PieVisualizationState) => - pieVisualization.getConfiguration({ - state: _state, - frame, - layerId: state.layers[0].layerId, - }); - - expect(findPrimaryGroup(getConfig(state))?.supportsMoreColumns).toBeFalsy(); + state.layers[0].metrics = colIds; + state.layers[0].allowMultipleMetrics = true; - const stateWithCollapsed = cloneDeep(state); - stateWithCollapsed.layers[0].collapseFns = { '1': 'sum' }; + const frame = mockFrame(); + frame.datasourceLayers[LAYER_ID]!.getTableSpec = () => + // reverse the column IDs in the datasource + colIds.reverse().map((id) => ({ columnId: id, fields: [] })); + + // this is to make sure the accessors get sorted before palette colors are applied + const palette = paletteServiceMock.get('default'); + palette.getCategoricalColor + .mockReturnValueOnce('color 1') + .mockReturnValueOnce('color 2') + .mockReturnValueOnce('color 3') + .mockReturnValueOnce('color 4'); + + const config = pieVisualization.getConfiguration({ + state, + frame, + layerId: state.layers[0].layerId, + }); - expect(findPrimaryGroup(getConfig(stateWithCollapsed))?.supportsMoreColumns).toBeTruthy(); + expect(findMetricGroup(config)?.accessors).toMatchInlineSnapshot(` + Array [ + Object { + "color": "color 1", + "columnId": "4", + "triggerIconType": "color", + }, + Object { + "color": "color 2", + "columnId": "3", + "triggerIconType": "color", + }, + Object { + "color": "color 3", + "columnId": "2", + "triggerIconType": "color", + }, + Object { + "color": "color 4", + "columnId": "1", + "triggerIconType": "color", + }, + ] + `); }); - it('counts multiple metrics toward the dimension limits when not mosaic', () => { - const colIds = new Array(PartitionChartsMeta.pie.maxBuckets - 1) - .fill(undefined) - .map((_, i) => String(i + 1)); + describe('dimension limits', () => { + it("doesn't count collapsed columns toward the dimension limits", () => { + const colIds = new Array(PartitionChartsMeta.pie.maxBuckets) + .fill(undefined) + .map((_, i) => String(i + 1)); - const frame = mockFrame(); - frame.datasourceLayers[LAYER_ID]!.getTableSpec = () => - colIds.map((id) => ({ columnId: id, fields: [] })); + const frame = mockFrame(); + frame.datasourceLayers[LAYER_ID]!.getTableSpec = () => + colIds.map((id) => ({ columnId: id, fields: [] })); - const state = getExampleState(); - state.layers[0].primaryGroups = colIds; - state.layers[0].allowMultipleMetrics = true; + const state = getExampleState(); + state.layers[0].primaryGroups = colIds; - const getConfig = (_state: PieVisualizationState) => - pieVisualization.getConfiguration({ - state: _state, - frame, - layerId: state.layers[0].layerId, - }); + const getConfig = (_state: PieVisualizationState) => + pieVisualization.getConfiguration({ + state: _state, + frame, + layerId: state.layers[0].layerId, + }); - expect(findPrimaryGroup(getConfig(state))?.supportsMoreColumns).toBeTruthy(); + expect(findPrimaryGroup(getConfig(state))?.supportsMoreColumns).toBeFalsy(); - const stateWithMultipleMetrics = cloneDeep(state); - stateWithMultipleMetrics.layers[0].metrics.push('1', '2'); + const stateWithCollapsed = cloneDeep(state); + stateWithCollapsed.layers[0].collapseFns = { '1': 'sum' }; - expect( - findPrimaryGroup(getConfig(stateWithMultipleMetrics))?.supportsMoreColumns - ).toBeFalsy(); - }); + expect(findPrimaryGroup(getConfig(stateWithCollapsed))?.supportsMoreColumns).toBeTruthy(); + }); - it('does NOT count multiple metrics toward the dimension limits when mosaic', () => { - const frame = mockFrame(); - frame.datasourceLayers[LAYER_ID]!.getTableSpec = () => []; + it('counts multiple metrics toward the dimension limits when not mosaic', () => { + const colIds = new Array(PartitionChartsMeta.pie.maxBuckets - 1) + .fill(undefined) + .map((_, i) => String(i + 1)); - const state = getExampleState(); - state.shape = 'mosaic'; - state.layers[0].primaryGroups = []; - state.layers[0].allowMultipleMetrics = false; // always true for mosaic + const frame = mockFrame(); + frame.datasourceLayers[LAYER_ID]!.getTableSpec = () => + colIds.map((id) => ({ columnId: id, fields: [] })); - const getConfig = (_state: PieVisualizationState) => - pieVisualization.getConfiguration({ - state: _state, - frame, - layerId: state.layers[0].layerId, - }); + const state = getExampleState(); + state.layers[0].primaryGroups = colIds; + state.layers[0].allowMultipleMetrics = true; - expect(findPrimaryGroup(getConfig(state))?.supportsMoreColumns).toBeTruthy(); + const getConfig = (_state: PieVisualizationState) => + pieVisualization.getConfiguration({ + state: _state, + frame, + layerId: state.layers[0].layerId, + }); - const stateWithMultipleMetrics = cloneDeep(state); - stateWithMultipleMetrics.layers[0].metrics.push('1', '2'); + expect(findPrimaryGroup(getConfig(state))?.supportsMoreColumns).toBeTruthy(); - expect( - findPrimaryGroup(getConfig(stateWithMultipleMetrics))?.supportsMoreColumns - ).toBeTruthy(); - }); + const stateWithMultipleMetrics = cloneDeep(state); + stateWithMultipleMetrics.layers[0].metrics.push('1', '2'); - it('reports too many metric dimensions if multiple not enabled', () => { - const colIds = ['1', '2', '3', '4']; + expect( + findPrimaryGroup(getConfig(stateWithMultipleMetrics))?.supportsMoreColumns + ).toBeFalsy(); + }); - const frame = mockFrame(); - frame.datasourceLayers[LAYER_ID]!.getTableSpec = () => - colIds.map((id) => ({ columnId: id, fields: [] })); + it('does NOT count multiple metrics toward the dimension limits when mosaic', () => { + const frame = mockFrame(); + frame.datasourceLayers[LAYER_ID]!.getTableSpec = () => []; - const state = getExampleState(); - state.layers[0].metrics = colIds; - state.layers[0].allowMultipleMetrics = false; - expect( - findMetricGroup( - pieVisualization.getConfiguration({ - state, - frame, - layerId: state.layers[0].layerId, - }) - )?.dimensionsTooMany - ).toBe(3); + const state = getExampleState(); + state.shape = 'mosaic'; + state.layers[0].primaryGroups = []; + state.layers[0].allowMultipleMetrics = false; // always true for mosaic - state.layers[0].allowMultipleMetrics = true; - expect( - findMetricGroup( + const getConfig = (_state: PieVisualizationState) => pieVisualization.getConfiguration({ - state, + state: _state, frame, layerId: state.layers[0].layerId, - }) - )?.dimensionsTooMany - ).toBe(0); + }); + + expect(findPrimaryGroup(getConfig(state))?.supportsMoreColumns).toBeTruthy(); + + const stateWithMultipleMetrics = cloneDeep(state); + stateWithMultipleMetrics.layers[0].metrics.push('1', '2'); + + expect( + findPrimaryGroup(getConfig(stateWithMultipleMetrics))?.supportsMoreColumns + ).toBeTruthy(); + }); + + it('reports too many metric dimensions if multiple not enabled', () => { + const colIds = ['1', '2', '3', '4']; + + const frame = mockFrame(); + frame.datasourceLayers[LAYER_ID]!.getTableSpec = () => + colIds.map((id) => ({ columnId: id, fields: [] })); + + const state = getExampleState(); + state.layers[0].metrics = colIds; + state.layers[0].allowMultipleMetrics = false; + expect( + findMetricGroup( + pieVisualization.getConfiguration({ + state, + frame, + layerId: state.layers[0].layerId, + }) + )?.dimensionsTooMany + ).toBe(3); + + state.layers[0].allowMultipleMetrics = true; + expect( + findMetricGroup( + pieVisualization.getConfiguration({ + state, + frame, + layerId: state.layers[0].layerId, + }) + )?.dimensionsTooMany + ).toBe(0); + }); }); it.each(Object.values(PieChartTypes).filter((type) => type !== 'mosaic'))( diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx index cedfb12f72df7..a5c218a6483e1 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx @@ -29,7 +29,7 @@ import type { } from '../../types'; import { getColumnToLabelMap, - getSortedGroups, + getSortedAccessorsForGroup, toExpression, toPreviewExpression, } from './to_expression'; @@ -91,12 +91,13 @@ export const getDefaultColorForMultiMetricDimension = ({ datasource: DatasourcePublicAPI | undefined; }) => { const columnToLabelMap = datasource ? getColumnToLabelMap(layer.metrics, datasource) : {}; + const sortedMetrics = getSortedAccessorsForGroup(datasource, layer, 'metrics'); return paletteService.get('default').getCategoricalColor([ { name: columnToLabelMap[columnId], - rankAtDepth: layer.metrics.indexOf(columnId), - totalSeriesAtDepth: layer.metrics.length, + rankAtDepth: sortedMetrics.indexOf(columnId), + totalSeriesAtDepth: sortedMetrics.length, }, ]) as string; }; @@ -167,7 +168,7 @@ export const getPieVisualization = ({ const datasource = frame.datasourceLayers[layer.layerId]; const getPrimaryGroupConfig = (): VisualizationDimensionGroupConfig => { - const originalOrder = getSortedGroups(datasource, layer); + const originalOrder = getSortedAccessorsForGroup(datasource, layer, 'primaryGroups'); // When we add a column it could be empty, and therefore have no order const accessors = originalOrder.map((accessor) => ({ columnId: accessor, @@ -273,7 +274,11 @@ export const getPieVisualization = ({ }; const getSecondaryGroupConfig = (): VisualizationDimensionGroupConfig | undefined => { - const originalSecondaryOrder = getSortedGroups(datasource, layer, 'secondaryGroups'); + const originalSecondaryOrder = getSortedAccessorsForGroup( + datasource, + layer, + 'secondaryGroups' + ); const accessors = originalSecondaryOrder.map((accessor) => ({ columnId: accessor, triggerIconType: isCollapsed(accessor, layer) ? 'aggregate' : undefined, @@ -317,7 +322,11 @@ export const getPieVisualization = ({ const getMetricGroupConfig = (): VisualizationDimensionGroupConfig => { const hasSliceBy = layer.primaryGroups.length + (layer.secondaryGroups?.length ?? 0); - const accessors: AccessorConfig[] = layer.metrics.map((columnId, index) => ({ + const accessors: AccessorConfig[] = getSortedAccessorsForGroup( + datasource, + layer, + 'metrics' + ).map((columnId) => ({ columnId, ...(layer.allowMultipleMetrics ? hasSliceBy @@ -371,7 +380,7 @@ export const getPieVisualization = ({ }; }, - setDimension({ prevState, layerId, columnId, groupId }) { + setDimension({ prevState, layerId, columnId, groupId, previousColumn }) { return { ...prevState, layers: prevState.layers.map((l) => { @@ -393,7 +402,8 @@ export const getPieVisualization = ({ ], }; } - return { ...l, metrics: [...l.metrics.filter((metric) => metric !== columnId), columnId] }; + const metrics = [...l.metrics.filter((metric) => metric !== columnId), columnId]; + return { ...l, metrics }; }), }; }, diff --git a/x-pack/plugins/maps/public/connected_components/timeslider/timeslider.tsx b/x-pack/plugins/maps/public/connected_components/timeslider/timeslider.tsx index a0756e35b4323..0d75eae9597a2 100644 --- a/x-pack/plugins/maps/public/connected_components/timeslider/timeslider.tsx +++ b/x-pack/plugins/maps/public/connected_components/timeslider/timeslider.tsx @@ -42,15 +42,17 @@ export class Timeslider extends Component { this._isMounted = true; } - _getInitialInput = async ( + _getCreationOptions = async ( initialInput: Partial, builder: typeof controlGroupInputBuilder ) => { builder.addTimeSliderControl(initialInput); return { - ...initialInput, - viewMode: ViewMode.VIEW, - timeRange: this.props.timeRange, + initialInput: { + ...initialInput, + viewMode: ViewMode.VIEW, + timeRange: this.props.timeRange, + }, }; }; @@ -91,7 +93,7 @@ export class Timeslider extends Component {
    diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/evaluate_stat.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/evaluate_stat.tsx index d62a22258cc01..df24f7cfac10c 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/evaluate_stat.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/evaluate_stat.tsx @@ -70,7 +70,7 @@ const tooltipContent = { 'xpack.ml.dataframe.analytics.regressionExploration.msleTooltipContent', { defaultMessage: - 'Average squared difference between the logarithm of the predicted values and the logarithm of the actual (ground truth) value', + 'Average squared difference between the logarithm of the predicted values and the logarithm of the actual (ground truth) value.', } ), [REGRESSION_STATS.R_SQUARED]: i18n.translate( diff --git a/x-pack/plugins/ml/public/index.ts b/x-pack/plugins/ml/public/index.ts index 4f9d99231c57b..ed99376a51c91 100755 --- a/x-pack/plugins/ml/public/index.ts +++ b/x-pack/plugins/ml/public/index.ts @@ -67,4 +67,7 @@ type AwaitReturnType = T extends PromiseLike ? U : T; export type GetMlSharedImportsReturnType = AwaitReturnType>; export { MLJobsAwaitingNodeWarning } from './application/components/jobs_awaiting_node_warning/new_job_awaiting_node_shared'; -export { MlNodeAvailableWarningShared } from './application/components/node_available_warning'; +export { + MlNodeAvailableWarningShared, + useMlNodeAvailableCheck, +} from './application/components/node_available_warning'; diff --git a/x-pack/plugins/ml/readme.md b/x-pack/plugins/ml/readme.md index b1b3ee8775dc2..c4b88780f6659 100644 --- a/x-pack/plugins/ml/readme.md +++ b/x-pack/plugins/ml/readme.md @@ -127,20 +127,21 @@ With PATH_TO_CONFIG and other options as follows. short tests | `test/functional/apps/ml/short_tests/config.ts` The `short tests` group contains tests for page navigation, model management, - feature controls, settings and embeddables. Test files for each group are located + feature controls, settings and notifications. Test files for each group are located in the directory of their configuration file. 1. Functional UI tests with `Basic` license: - - PATH_TO_CONFIG: `test/functional_basic/config.ts` - - Add `--include-tag ml` to the test runner command - - Tests are located in `x-pack/test/functional_basic/apps/ml` + Group | PATH_TO_CONFIG + ----- | -------------- + permissions | `test/functional_basic/apps/ml/permissions/config.ts` + data visualizer group1 (file data viz) | `test/functional_basic/apps/ml/data_visualizer/group3/config.ts` + data visualizer group2 (index data viz) | `test/functional_basic/apps/ml/data_visualizer/group2/config.ts` + data visualizer group3 (actions panel, discover grid) | `test/functional_basic/apps/ml/data_visualizer/group3/config.ts` 1. API integration tests with `Trial` license: - - PATH_TO_CONFIG: `test/api_integration/config.ts` - - Add `--include-tag ml` to the test runner command - - Tests are located in `x-pack/test/api_integration/apis/ml` + - PATH_TO_CONFIG: `test/api_integration/apis/ml/config.ts` 1. Accessibility tests: diff --git a/x-pack/plugins/observability/dev_docs/slo.md b/x-pack/plugins/observability/dev_docs/slo.md index bd9dcc96c4c59..595eac175ae57 100644 --- a/x-pack/plugins/observability/dev_docs/slo.md +++ b/x-pack/plugins/observability/dev_docs/slo.md @@ -6,11 +6,11 @@ Add the feature flag: `xpack.observability.unsafe.slo.enabled: true` in your Kib We currently support the following SLI: -- APM Transaction Error Rate (Availability) -- APM Transaction Duration (Latency) +- APM Transaction Error Rate, known as APM Availability +- APM Transaction Duration, known as APM Latency - Custom KQL -For the APM SLIs, customer can provide the service, environment, transaction name and type to configure them. For the **Duration** SLI, a threshold in microsecond needs to be provided to discriminate the good and bad responses (events). For the **Error Rate** SLI, a list of good status codes needs to be provided to discriminate the good and bad responses (events). +For the APM SLIs, customer can provide the service, environment, transaction name and type to configure them. For the **APM Latency** SLI, a threshold in milliseconds needs to be provided to discriminate the good and bad responses (events). For the **APM Availability** SLI, a list of good status codes needs to be provided to discriminate the good and bad responses (events). The API supports an optional kql filter to further filter the apm data. The **custom KQL** SLI requires an index pattern, an optional filter query, a numerator query, and denominator query. @@ -43,9 +43,9 @@ If a **timeslices** budgeting method is used, we also need to define the **times The default settings should be sufficient for most users, but if needed, the following properties can be overwritten: -- timestampField: The date time field to use from the source index -- syncDelay: The ingest delay in the source data -- frequency: How often do we query the source data +- **timestampField**: The date time field to use from the source index +- **syncDelay**: The ingest delay in the source data +- **frequency**: How often do we query the source data ## Example diff --git a/x-pack/plugins/observability/docs/openapi/slo/README.md b/x-pack/plugins/observability/docs/openapi/slo/README.md new file mode 100644 index 0000000000000..e128dd32a38b9 --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/README.md @@ -0,0 +1,34 @@ +# OpenAPI (Experimental) + +The current self-contained spec file is [as YAML](https://raw.githubusercontent.com/elastic/kibana/master/x-pack/plugins/obserbability/docs/openapi/slo/bundled.yaml) and can be used for online tools like those found at . +This spec is experimental and may be incomplete or change later. + +A guide about the OpenApi specification can be found at [https://swagger.io/docs/specification/about/](https://swagger.io/docs/specification/about/). + +## The `openapi/slo` folder + +* `entrypoint.yaml` is the overview file which pulls together all the paths and components. +* [Paths](paths/README.md): this defines each endpoint. A path can have one operation per http method. +* [Components](components/README.md): Reusable components + +## Tools + +It is possible to validate the docs before bundling them with the following +command in the `x-pack/plugins/observability/docs/openapi/slo` folder: + + ```bash + npx swagger-cli validate entrypoint.yaml + ``` + +Then you can generate the `bundled` files by running the following commands: + + ```bash + npx @redocly/cli bundle entrypoint.yaml --output bundled.yaml --ext yaml + npx @redocly/cli bundle entrypoint.yaml --output bundled.json --ext json + ``` + +After generating the json bundle ensure that it is also valid by running the following command: + + ```bash + npx @redocly/cli lint bundled.json + ``` diff --git a/x-pack/plugins/observability/docs/openapi/slo/bundled.yaml b/x-pack/plugins/observability/docs/openapi/slo/bundled.yaml new file mode 100644 index 0000000000000..9e333d0594c66 --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/bundled.yaml @@ -0,0 +1,798 @@ +openapi: 3.0.1 +info: + title: SLOs + description: OpenAPI schema for SLOs endpoints + version: '0.1' + contact: + name: Actionable Observability Team + license: + name: Elastic License 2.0 + url: https://www.elastic.co/licensing/elastic-license +tags: + - name: slos + description: SLO APIs enable you to define, manage and track service-level objectives +servers: + - url: http://localhost:5601 + description: local +paths: + /s/{spaceId}/api/observability/slos: + post: + summary: Creates an SLO. + operationId: createSlo + description: | + You must have `all` privileges for the **SLOs** feature in the **Observability** section of the Kibana feature privileges. + tags: + - slos + parameters: + - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/components/parameters/space_id' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/create_slo_request' + responses: + '200': + description: Successful request + content: + application/json: + schema: + $ref: '#/components/schemas/create_slo_response' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/4xx_response' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '#/components/schemas/4xx_response' + servers: + - url: https://localhost:5601 + get: + summary: Retrieves a paginated list of SLOs + operationId: findSlos + description: | + You must have the `read` privileges for the **SLOs** feature in the **Observability** section of the Kibana feature privileges. + tags: + - slos + parameters: + - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/components/parameters/space_id' + - name: name + in: query + description: Filter by name + schema: + type: string + example: awesome-service + - name: indicatorTypes + in: query + description: Filter by indicator type + schema: + type: array + items: + type: string + example: + - sli.kql.custom + - name: page + in: query + description: The page number to return + schema: + type: integer + default: 1 + example: 1 + - name: perPage + in: query + description: The number of SLOs to return per page + schema: + type: integer + default: 25 + example: 20 + - name: sortBy + in: query + description: Sort by field + schema: + type: string + enum: + - name + - indicatorType + default: name + example: name + - name: sortDirection + in: query + description: Sort order + schema: + type: string + enum: + - asc + - desc + default: asc + example: asc + responses: + '200': + description: Successful request + content: + application/json: + schema: + $ref: '#/components/schemas/find_slo_response' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/4xx_response' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '#/components/schemas/4xx_response' + /s/{spaceId}/api/observability/slos/{sloId}: + get: + summary: Retrieves a SLO + operationId: getSlo + description: | + You must have the `read` privileges for the **SLOs** feature in the **Observability** section of the Kibana feature privileges. + tags: + - slos + parameters: + - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/components/parameters/space_id' + - $ref: '#/components/parameters/slo_id' + responses: + '200': + description: Succesful request + content: + application/json: + schema: + $ref: '#/components/schemas/slo_response' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/4xx_response' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '#/components/schemas/4xx_response' + put: + summary: Updates an SLO + operationId: updateSlo + description: | + You must have the `write` privileges for the **SLOs** feature in the **Observability** section of the Kibana feature privileges. + tags: + - slos + parameters: + - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/components/parameters/space_id' + - $ref: '#/components/parameters/slo_id' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/update_slo_request' + responses: + '200': + description: Succesful request + content: + application/json: + schema: + $ref: '#/components/schemas/slo_response' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/4xx_response' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '#/components/schemas/4xx_response' + delete: + summary: Deletes an SLO + operationId: deleteSlo + description: | + You must have the `write` privileges for the **SLOs** feature in the **Observability** section of the Kibana feature privileges. + tags: + - slos + parameters: + - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/components/parameters/space_id' + - $ref: '#/components/parameters/slo_id' + responses: + '204': + description: Succesful request + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/4xx_response' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '#/components/schemas/4xx_response' + /s/{spaceId}/api/observability/slos/{sloId}/enable: + post: + summary: Enables an SLO + operationId: enableSlo + description: | + You must have the `write` privileges for the **SLOs** feature in the **Observability** section of the Kibana feature privileges. + tags: + - slos + parameters: + - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/components/parameters/space_id' + - $ref: '#/components/parameters/slo_id' + responses: + '204': + description: Succesful request + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/4xx_response' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '#/components/schemas/4xx_response' + /s/{spaceId}/api/observability/slos/{sloId}/disable: + post: + summary: Disables an SLO + operationId: disableSlo + description: | + You must have the `write` privileges for the **SLOs** feature in the **Observability** section of the Kibana feature privileges. + tags: + - slos + parameters: + - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/components/parameters/space_id' + - $ref: '#/components/parameters/slo_id' + responses: + '200': + description: Succesful request + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/4xx_response' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '#/components/schemas/4xx_response' + /s/{spaceId}/internal/observability/slos/_historical_summary: + post: + summary: Retrieves the historical summary for a list of SLOs + operationId: historicalSummary + description: | + You must have the `read` privileges for the **SLOs** feature in the **Observability** section of the Kibana feature privileges. + tags: + - slos + parameters: + - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/components/parameters/space_id' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/historical_summary_request' + responses: + '200': + description: Successful request + content: + application/json: + schema: + $ref: '#/components/schemas/historical_summary_response' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/4xx_response' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '#/components/schemas/4xx_response' +components: + securitySchemes: + basicAuth: + type: http + scheme: basic + apiKeyAuth: + type: apiKey + in: header + name: ApiKey + parameters: + kbn_xsrf: + schema: + type: string + in: header + name: kbn-xsrf + description: Cross-site request forgery protection + required: true + space_id: + in: path + name: spaceId + description: An identifier for the space. If `/s/` and the identifier are omitted from the path, the default space is used. + required: true + schema: + type: string + example: default + slo_id: + in: path + name: sloId + description: An identifier for the slo. + required: true + schema: + type: string + example: 9c235211-6834-11ea-a78c-6feb38a34414 + schemas: + indicator_properties_custom_kql: + title: Custom KQL indicator type definition + required: + - type + - params + description: Defines properties for a custom KQL indicator type + type: object + properties: + params: + description: An object containing the indicator parameters. + type: object + nullable: false + required: + - index + properties: + index: + description: The index or index pattern to use + type: string + example: my-service-* + filter: + description: the KQL query to filter the documents with. + type: string + example: 'field.environment : "production" and service.name : "my-service"' + good: + description: the KQL query used to define the good events. + type: string + example: 'request.latency <= 150 and request.status_code : "2xx"' + total: + description: the KQL query used to define all events. + type: string + example: '' + type: + description: The type of indicator. + type: string + example: sli.kql.custom + indicator_properties_apm_availability: + title: APM availability indicator type definition + required: + - type + - params + description: Defines properties for the APM availability indicator type + type: object + properties: + params: + description: An object containing the indicator parameters. + type: object + nullable: false + required: + - service + - environment + - transactionType + - transactionName + properties: + service: + description: The APM service name + type: string + example: o11y-app + environment: + description: The APM service environment or "*" + type: string + example: production + transactionType: + description: The APM transaction type or "*" + type: string + example: request + transactionName: + description: The APM transaction name or "*" + type: string + example: GET /my/api + goodStatusCodes: + description: The status codes considered as good events. Default to 2xx, 3xx and 4xx + type: array + items: + type: string + example: + - 2xx + - 3xx + - 4xx + filter: + description: KQL query used for filtering the data + type: string + example: 'service.foo : "bar"' + index: + description: The index used by APM metrics + type: string + example: metrics-apm*,apm* + type: + description: The type of indicator. + type: string + example: sli.apm.transactionDuration + indicator_properties_apm_latency: + title: APM latency indicator type definition + required: + - type + - params + description: Defines properties for the APM latency indicator type + type: object + properties: + params: + description: An object containing the indicator parameters. + type: object + nullable: false + required: + - service + - environment + - transactionType + - transactionName + properties: + service: + description: The APM service name + type: string + example: o11y-app + environment: + description: The APM service environment or "*" + type: string + example: production + transactionType: + description: The APM transaction type or "*" + type: string + example: request + transactionName: + description: The APM transaction name or "*" + type: string + example: GET /my/api + filter: + description: KQL query used for filtering the data + type: string + example: 'service.foo : "bar"' + index: + description: The index used by APM metrics + type: string + example: metrics-apm*,apm* + type: + description: The type of indicator. + type: string + example: sli.apm.transactionDuration + time_window_rolling: + title: Rolling time window definition + required: + - duration + - isRolling + description: Defines properties for rolling time window + type: object + properties: + duration: + description: the duration formatted as {duration}{unit} + type: string + example: 28d + isRolling: + description: Indicates a rolling time window + type: boolean + example: true + time_window_calendar_aligned: + title: Calendar aligned time window definition + required: + - duration + - calendar + description: Defines properties for calendar aligned time window + type: object + properties: + duration: + description: the duration formatted as {duration}{unit} + type: string + example: 1M + calendar: + description: Defines the calendar start date + type: object + properties: + startTime: + description: The start date to use. + type: string + example: '2022-01-01T08:00:00.000Z' + budgeting_method: + title: Budgeting method + type: string + description: The budgeting method to use + enum: + - occurrences + - timeslices + example: occurrences + objective: + title: Objective definition + required: + - target + description: Defines properties for objective + type: object + properties: + target: + description: the target objective between 0 and 1 excluded + type: number + example: 0.99 + timeslicesTarget: + description: the target objective for each slice when using a timeslices budgeting method + type: number + example: 0.995 + timeslicesWindow: + description: the duration of each slice when using a timeslices budgeting method, as {duraton}{unit} + type: string + example: 5m + settings: + title: Settings definition + description: Defines properties for settings. + type: object + properties: + timestampField: + description: | + The timestamp field used in the source indice. Particularly useful for custom kql indicator type, when the index does not use the default '@timestamp' field + type: string + example: timestamp + syncDelay: + description: The synch delay to apply to the transform. Default 1m + type: string + example: 5m + frequency: + description: Configure how often the transform runs, default 1m + type: string + example: 5m + error_budget: + title: Error budget definition + type: object + properties: + initial: + type: number + description: The initial error budget, as 1 - objective + example: 0.02 + consumed: + type: number + description: The error budget consummed, as a percentage of the initial value. + example: 0.8 + remaining: + type: number + description: The error budget remaining, as a percentage of the initial value. + example: 0.2 + isEstimated: + type: boolean + description: Only for SLO defined with occurrences budgeting method and calendar aligned time window. + example: true + summary: + title: Summary definition + type: object + properties: + status: + type: string + enum: + - NO_DATA + - HEALTHY + - DEGRADING + - VIOLATED + example: HEALTHY + sliValue: + type: number + example: 0.9836 + errorBudget: + $ref: '#/components/schemas/error_budget' + slo_response: + title: SLO Response + type: object + properties: + id: + description: The identifier of the SLO. + type: string + example: 8853df00-ae2e-11ed-90af-09bb6422b258 + name: + description: The name of the SLO. + type: string + example: My Service SLO + description: + description: The description of the SLO. + type: string + example: My SLO description + indicator: + oneOf: + - $ref: '#/components/schemas/indicator_properties_custom_kql' + - $ref: '#/components/schemas/indicator_properties_apm_availability' + - $ref: '#/components/schemas/indicator_properties_apm_latency' + timeWindow: + oneOf: + - $ref: '#/components/schemas/time_window_rolling' + - $ref: '#/components/schemas/time_window_calendar_aligned' + budgetingMethod: + $ref: '#/components/schemas/budgeting_method' + objective: + $ref: '#/components/schemas/objective' + settings: + $ref: '#/components/schemas/settings' + revision: + description: The SLO revision + type: number + example: 2 + summary: + $ref: '#/components/schemas/summary' + enabled: + description: Indicate if the SLO is enabled + type: boolean + example: true + createdAt: + description: The creation date + type: string + example: '2023-01-12T10:03:19.000Z' + updatedAt: + description: The last update date + type: string + example: '2023-01-12T10:03:19.000Z' + find_slo_response: + title: Find SLO response + description: | + A paginated response of SLOs matching the query. + type: object + properties: + page: + type: number + example: 1 + perPage: + type: number + example: 25 + total: + type: number + example: 34 + results: + type: array + items: + $ref: '#/components/schemas/slo_response' + 4xx_response: + title: Bad response + type: object + required: + - statusCode + - error + - message + properties: + statusCode: + type: number + example: 401 + error: + type: string + example: Unauthorized + message: + type: string + example: "[security_exception\n\tRoot causes:\n\t\tsecurity_exception: unable to authenticate user [elastics] for REST request [/_security/_authenticate]]: unable to authenticate user [elastics] for REST request [/_security/_authenticate]" + create_slo_request: + title: Create SLO request + description: | + The create SLO API request body varies depending on the type of indicator, time window and budgeting method. + type: object + required: + - name + - description + - indicator + - timeWindow + - budgetingMethod + - objective + properties: + name: + description: A name for the SLO. + type: string + description: + description: A description for the SLO. + type: string + indicator: + oneOf: + - $ref: '#/components/schemas/indicator_properties_custom_kql' + - $ref: '#/components/schemas/indicator_properties_apm_availability' + - $ref: '#/components/schemas/indicator_properties_apm_latency' + timeWindow: + oneOf: + - $ref: '#/components/schemas/time_window_rolling' + - $ref: '#/components/schemas/time_window_calendar_aligned' + budgetingMethod: + $ref: '#/components/schemas/budgeting_method' + objective: + $ref: '#/components/schemas/objective' + settings: + $ref: '#/components/schemas/settings' + create_slo_response: + title: Create SLO response + type: object + required: + - id + properties: + id: + type: string + example: 8853df00-ae2e-11ed-90af-09bb6422b258 + update_slo_request: + title: Update SLO request + description: | + The update SLO API request body varies depending on the type of indicator, time window and budgeting method. Partial update is handled. + type: object + properties: + name: + description: A name for the SLO. + type: string + description: + description: A description for the SLO. + type: string + indicator: + oneOf: + - $ref: '#/components/schemas/indicator_properties_custom_kql' + - $ref: '#/components/schemas/indicator_properties_apm_availability' + - $ref: '#/components/schemas/indicator_properties_apm_latency' + timeWindow: + oneOf: + - $ref: '#/components/schemas/time_window_rolling' + - $ref: '#/components/schemas/time_window_calendar_aligned' + budgetingMethod: + $ref: '#/components/schemas/budgeting_method' + objective: + $ref: '#/components/schemas/objective' + settings: + $ref: '#/components/schemas/settings' + historical_summary_request: + title: Historical summary request + type: object + required: + - sloIds + properties: + sloIds: + description: The list of SLO identifiers to get the historical summary for + type: array + items: + type: string + example: 8853df00-ae2e-11ed-90af-09bb6422b258 + historical_summary_response: + title: Historical summary response + type: object + additionalProperties: + type: array + items: + type: object + properties: + date: + type: string + example: '2022-01-01T00:00:00.000Z' + status: + type: string + enum: + - NO_DATA + - HEALTHY + - DEGRADING + - VIOLATED + example: HEALTHY + sliValue: + type: number + example: 0.9836 + errorBudget: + $ref: '#/components/schemas/error_budget' +security: + - basicAuth: [] + - apiKeyAuth: [] diff --git a/x-pack/plugins/observability/docs/openapi/slo/components/README.md b/x-pack/plugins/observability/docs/openapi/slo/components/README.md new file mode 100644 index 0000000000000..0841562a33150 --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/components/README.md @@ -0,0 +1,7 @@ +Reusable components +=========== + + - `examples` - reusable [Example objects](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.2.md#exampleObject) + - `headers` - reusable [Header objects](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#headerObject) + - `parameters` - reusable [Parameter objects](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#parameterObject) + - `schemas` - reusable [Schema objects](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.2.md#schemaObject) diff --git a/x-pack/plugins/observability/docs/openapi/slo/components/headers/kbn_xsrf.yaml b/x-pack/plugins/observability/docs/openapi/slo/components/headers/kbn_xsrf.yaml new file mode 100644 index 0000000000000..fe0402a43aa03 --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/components/headers/kbn_xsrf.yaml @@ -0,0 +1,6 @@ +schema: + type: string +in: header +name: kbn-xsrf +description: Cross-site request forgery protection +required: true diff --git a/x-pack/plugins/observability/docs/openapi/slo/components/parameters/slo_id.yaml b/x-pack/plugins/observability/docs/openapi/slo/components/parameters/slo_id.yaml new file mode 100644 index 0000000000000..9b687ee1b0b05 --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/components/parameters/slo_id.yaml @@ -0,0 +1,7 @@ +in: path +name: sloId +description: An identifier for the slo. +required: true +schema: + type: string + example: 9c235211-6834-11ea-a78c-6feb38a34414 diff --git a/x-pack/plugins/observability/docs/openapi/slo/components/parameters/space_id.yaml b/x-pack/plugins/observability/docs/openapi/slo/components/parameters/space_id.yaml new file mode 100644 index 0000000000000..0a9fba457e3e7 --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/components/parameters/space_id.yaml @@ -0,0 +1,7 @@ +in: path +name: spaceId +description: An identifier for the space. If `/s/` and the identifier are omitted from the path, the default space is used. +required: true +schema: + type: string + example: default diff --git a/x-pack/plugins/observability/docs/openapi/slo/components/schemas/4xx_response.yaml b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/4xx_response.yaml new file mode 100644 index 0000000000000..caf2d3a50d53a --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/4xx_response.yaml @@ -0,0 +1,16 @@ +title: Bad response +type: object +required: + - statusCode + - error + - message +properties: + statusCode: + type: number + example: 401 + error: + type: string + example: Unauthorized + message: + type: string + example: "[security_exception\n\tRoot causes:\n\t\tsecurity_exception: unable to authenticate user [elastics] for REST request [/_security/_authenticate]]: unable to authenticate user [elastics] for REST request [/_security/_authenticate]" diff --git a/x-pack/plugins/observability/docs/openapi/slo/components/schemas/budgeting_method.yaml b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/budgeting_method.yaml new file mode 100644 index 0000000000000..8b3957ddfa1cb --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/budgeting_method.yaml @@ -0,0 +1,7 @@ +title: Budgeting method +type: string +description: The budgeting method to use +enum: + - occurrences + - timeslices +example: occurrences \ No newline at end of file diff --git a/x-pack/plugins/observability/docs/openapi/slo/components/schemas/create_slo_request.yaml b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/create_slo_request.yaml new file mode 100644 index 0000000000000..e4321039189e1 --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/create_slo_request.yaml @@ -0,0 +1,33 @@ +title: Create SLO request +description: > + The create SLO API request body varies depending on the type of indicator, time window and budgeting method. +type: object +required: + - name + - description + - indicator + - timeWindow + - budgetingMethod + - objective +properties: + name: + description: A name for the SLO. + type: string + description: + description: A description for the SLO. + type: string + indicator: + oneOf: + - $ref: 'indicator_properties_custom_kql.yaml' + - $ref: 'indicator_properties_apm_availability.yaml' + - $ref: 'indicator_properties_apm_latency.yaml' + timeWindow: + oneOf: + - $ref: 'time_window_rolling.yaml' + - $ref: 'time_window_calendar_aligned.yaml' + budgetingMethod: + $ref: 'budgeting_method.yaml' + objective: + $ref: 'objective.yaml' + settings: + $ref: 'settings.yaml' diff --git a/x-pack/plugins/observability/docs/openapi/slo/components/schemas/create_slo_response.yaml b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/create_slo_response.yaml new file mode 100644 index 0000000000000..ce01f2cae39b8 --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/create_slo_response.yaml @@ -0,0 +1,8 @@ +title: Create SLO response +type: object +required: + - id +properties: + id: + type: string + example: 8853df00-ae2e-11ed-90af-09bb6422b258 diff --git a/x-pack/plugins/observability/docs/openapi/slo/components/schemas/error_budget.yaml b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/error_budget.yaml new file mode 100644 index 0000000000000..67977a2c44931 --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/error_budget.yaml @@ -0,0 +1,19 @@ +title: Error budget definition +type: object +properties: + initial: + type: number + description: The initial error budget, as 1 - objective + example: 0.02 + consumed: + type: number + description: The error budget consummed, as a percentage of the initial value. + example: 0.80 + remaining: + type: number + description: The error budget remaining, as a percentage of the initial value. + example: 0.20 + isEstimated: + type: boolean + description: Only for SLO defined with occurrences budgeting method and calendar aligned time window. + example: true \ No newline at end of file diff --git a/x-pack/plugins/observability/docs/openapi/slo/components/schemas/find_slo_response.yaml b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/find_slo_response.yaml new file mode 100644 index 0000000000000..36a701efa34f4 --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/find_slo_response.yaml @@ -0,0 +1,18 @@ +title: Find SLO response +description: > + A paginated response of SLOs matching the query. +type: object +properties: + page: + type: number + example: 1 + perPage: + type: number + example: 25 + total: + type: number + example: 34 + results: + type: array + items: + $ref: 'slo_response.yaml' \ No newline at end of file diff --git a/x-pack/plugins/observability/docs/openapi/slo/components/schemas/historical_summary_request.yaml b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/historical_summary_request.yaml new file mode 100644 index 0000000000000..737a5b83f03f9 --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/historical_summary_request.yaml @@ -0,0 +1,12 @@ +title: Historical summary request +type: object +required: + - sloIds +properties: + sloIds: + description: The list of SLO identifiers to get the historical summary for + type: array + items: + type: string + example: 8853df00-ae2e-11ed-90af-09bb6422b258 + \ No newline at end of file diff --git a/x-pack/plugins/observability/docs/openapi/slo/components/schemas/historical_summary_response.yaml b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/historical_summary_response.yaml new file mode 100644 index 0000000000000..923d6acbeaa93 --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/historical_summary_response.yaml @@ -0,0 +1,23 @@ +title: Historical summary response +type: object +additionalProperties: + type: array + items: + type: object + properties: + date: + type: string + example: "2022-01-01T00:00:00.000Z" + status: + type: string + enum: + - NO_DATA + - HEALTHY + - DEGRADING + - VIOLATED + example: "HEALTHY" + sliValue: + type: number + example: 0.9836 + errorBudget: + $ref: 'error_budget.yaml' \ No newline at end of file diff --git a/x-pack/plugins/observability/docs/openapi/slo/components/schemas/indicator_properties_apm_availability.yaml b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/indicator_properties_apm_availability.yaml new file mode 100644 index 0000000000000..29eca24b8df3b --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/indicator_properties_apm_availability.yaml @@ -0,0 +1,54 @@ +title: APM availability indicator type definition +required: + - type + - params +description: Defines properties for the APM availability indicator type +type: object +properties: + params: + description: An object containing the indicator parameters. + type: object + nullable: false + required: + - service + - environment + - transactionType + - transactionName + properties: + service: + description: The APM service name + type: string + example: o11y-app + environment: + description: The APM service environment or "*" + type: string + example: production + transactionType: + description: The APM transaction type or "*" + type: string + example: request + transactionName: + description: The APM transaction name or "*" + type: string + example: GET /my/api + goodStatusCodes: + description: The status codes considered as good events. Default to 2xx, 3xx and 4xx + type: array + items: + type: string + example: + - "2xx" + - "3xx" + - "4xx" + filter: + description: KQL query used for filtering the data + type: string + example: 'service.foo : "bar"' + index: + description: The index used by APM metrics + type: string + example: metrics-apm*,apm* + type: + description: The type of indicator. + type: string + example: sli.apm.transactionDuration diff --git a/x-pack/plugins/observability/docs/openapi/slo/components/schemas/indicator_properties_apm_latency.yaml b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/indicator_properties_apm_latency.yaml new file mode 100644 index 0000000000000..7ec3bf40871d8 --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/indicator_properties_apm_latency.yaml @@ -0,0 +1,45 @@ +title: APM latency indicator type definition +required: + - type + - params +description: Defines properties for the APM latency indicator type +type: object +properties: + params: + description: An object containing the indicator parameters. + type: object + nullable: false + required: + - service + - environment + - transactionType + - transactionName + properties: + service: + description: The APM service name + type: string + example: o11y-app + environment: + description: The APM service environment or "*" + type: string + example: production + transactionType: + description: The APM transaction type or "*" + type: string + example: request + transactionName: + description: The APM transaction name or "*" + type: string + example: GET /my/api + filter: + description: KQL query used for filtering the data + type: string + example: 'service.foo : "bar"' + index: + description: The index used by APM metrics + type: string + example: metrics-apm*,apm* + type: + description: The type of indicator. + type: string + example: sli.apm.transactionDuration diff --git a/x-pack/plugins/observability/docs/openapi/slo/components/schemas/indicator_properties_custom_kql.yaml b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/indicator_properties_custom_kql.yaml new file mode 100644 index 0000000000000..e5ef6cddf858a --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/indicator_properties_custom_kql.yaml @@ -0,0 +1,34 @@ +title: Custom KQL indicator type definition +required: + - type + - params +description: Defines properties for a custom KQL indicator type +type: object +properties: + params: + description: An object containing the indicator parameters. + type: object + nullable: false + required: + - index + properties: + index: + description: The index or index pattern to use + type: string + example: my-service-* + filter: + description: the KQL query to filter the documents with. + type: string + example: 'field.environment : "production" and service.name : "my-service"' + good: + description: the KQL query used to define the good events. + type: string + example: 'request.latency <= 150 and request.status_code : "2xx"' + total: + description: the KQL query used to define all events. + type: string + example: '' + type: + description: The type of indicator. + type: string + example: sli.kql.custom diff --git a/x-pack/plugins/observability/docs/openapi/slo/components/schemas/objective.yaml b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/objective.yaml new file mode 100644 index 0000000000000..b64c3cc0dd15a --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/objective.yaml @@ -0,0 +1,18 @@ +title: Objective definition +required: + - target +description: Defines properties for objective +type: object +properties: + target: + description: the target objective between 0 and 1 excluded + type: number + example: 0.99 + timeslicesTarget: + description: the target objective for each slice when using a timeslices budgeting method + type: number + example: 0.995 + timeslicesWindow: + description: the duration of each slice when using a timeslices budgeting method, as {duraton}{unit} + type: string + example: 5m diff --git a/x-pack/plugins/observability/docs/openapi/slo/components/schemas/settings.yaml b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/settings.yaml new file mode 100644 index 0000000000000..27459b205d981 --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/settings.yaml @@ -0,0 +1,18 @@ +title: Settings definition +description: Defines properties for settings. +type: object +properties: + timestampField: + description: > + The timestamp field used in the source indice. Particularly useful for custom kql indicator type, when the index + does not use the default '@timestamp' field + type: string + example: timestamp + syncDelay: + description: The synch delay to apply to the transform. Default 1m + type: string + example: 5m + frequency: + description: Configure how often the transform runs, default 1m + type: string + example: 5m diff --git a/x-pack/plugins/observability/docs/openapi/slo/components/schemas/slo_response.yaml b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/slo_response.yaml new file mode 100644 index 0000000000000..ffd0e23f872d5 --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/slo_response.yaml @@ -0,0 +1,48 @@ +title: SLO Response +type: object +properties: + id: + description: The identifier of the SLO. + type: string + example: 8853df00-ae2e-11ed-90af-09bb6422b258 + name: + description: The name of the SLO. + type: string + example: My Service SLO + description: + description: The description of the SLO. + type: string + example: My SLO description + indicator: + oneOf: + - $ref: 'indicator_properties_custom_kql.yaml' + - $ref: 'indicator_properties_apm_availability.yaml' + - $ref: 'indicator_properties_apm_latency.yaml' + timeWindow: + oneOf: + - $ref: 'time_window_rolling.yaml' + - $ref: 'time_window_calendar_aligned.yaml' + budgetingMethod: + $ref: 'budgeting_method.yaml' + objective: + $ref: 'objective.yaml' + settings: + $ref: 'settings.yaml' + revision: + description: The SLO revision + type: number + example: 2 + summary: + $ref: 'summary.yaml' + enabled: + description: Indicate if the SLO is enabled + type: boolean + example: true + createdAt: + description: The creation date + type: string + example: "2023-01-12T10:03:19.000Z" + updatedAt: + description: The last update date + type: string + example: "2023-01-12T10:03:19.000Z" \ No newline at end of file diff --git a/x-pack/plugins/observability/docs/openapi/slo/components/schemas/summary.yaml b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/summary.yaml new file mode 100644 index 0000000000000..67662c222fae0 --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/summary.yaml @@ -0,0 +1,16 @@ +title: Summary definition +type: object +properties: + status: + type: string + enum: + - NO_DATA + - HEALTHY + - DEGRADING + - VIOLATED + example: "HEALTHY" + sliValue: + type: number + example: 0.9836 + errorBudget: + $ref: 'error_budget.yaml' \ No newline at end of file diff --git a/x-pack/plugins/observability/docs/openapi/slo/components/schemas/time_window_calendar_aligned.yaml b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/time_window_calendar_aligned.yaml new file mode 100644 index 0000000000000..4c494e97110c9 --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/time_window_calendar_aligned.yaml @@ -0,0 +1,19 @@ +title: Calendar aligned time window definition +required: + - duration + - calendar +description: Defines properties for calendar aligned time window +type: object +properties: + duration: + description: the duration formatted as {duration}{unit} + type: string + example: 1M + calendar: + description: Defines the calendar start date + type: object + properties: + startTime: + description: The start date to use. + type: string + example: "2022-01-01T08:00:00.000Z" diff --git a/x-pack/plugins/observability/docs/openapi/slo/components/schemas/time_window_rolling.yaml b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/time_window_rolling.yaml new file mode 100644 index 0000000000000..9ee0060101e67 --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/time_window_rolling.yaml @@ -0,0 +1,15 @@ +title: Rolling time window definition +required: + - duration + - isRolling +description: Defines properties for rolling time window +type: object +properties: + duration: + description: the duration formatted as {duration}{unit} + type: string + example: 28d + isRolling: + description: Indicates a rolling time window + type: boolean + example: true diff --git a/x-pack/plugins/observability/docs/openapi/slo/components/schemas/update_slo_request.yaml b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/update_slo_request.yaml new file mode 100644 index 0000000000000..19c04d62f9b82 --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/update_slo_request.yaml @@ -0,0 +1,27 @@ +title: Update SLO request +description: > + The update SLO API request body varies depending on the type of indicator, time window and budgeting method. + Partial update is handled. +type: object +properties: + name: + description: A name for the SLO. + type: string + description: + description: A description for the SLO. + type: string + indicator: + oneOf: + - $ref: 'indicator_properties_custom_kql.yaml' + - $ref: 'indicator_properties_apm_availability.yaml' + - $ref: 'indicator_properties_apm_latency.yaml' + timeWindow: + oneOf: + - $ref: 'time_window_rolling.yaml' + - $ref: 'time_window_calendar_aligned.yaml' + budgetingMethod: + $ref: 'budgeting_method.yaml' + objective: + $ref: 'objective.yaml' + settings: + $ref: 'settings.yaml' diff --git a/x-pack/plugins/observability/docs/openapi/slo/entrypoint.yaml b/x-pack/plugins/observability/docs/openapi/slo/entrypoint.yaml new file mode 100644 index 0000000000000..f0e5812ed7688 --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/entrypoint.yaml @@ -0,0 +1,39 @@ +openapi: 3.0.1 +info: + title: SLOs + description: OpenAPI schema for SLOs endpoints + version: '0.1' + contact: + name: Actionable Observability Team + license: + name: Elastic License 2.0 + url: https://www.elastic.co/licensing/elastic-license +tags: + - name: slos + description: SLO APIs enable you to define, manage and track service-level objectives +servers: + - url: 'http://localhost:5601' + description: local +paths: + '/s/{spaceId}/api/observability/slos': + $ref: 'paths/s@{spaceid}@api@slos.yaml' + '/s/{spaceId}/api/observability/slos/{sloId}': + $ref: 'paths/s@{spaceid}@api@slos@{sloid}.yaml' + '/s/{spaceId}/api/observability/slos/{sloId}/enable': + $ref: 'paths/s@{spaceid}@api@slos@{sloid}@{enable}.yaml' + '/s/{spaceId}/api/observability/slos/{sloId}/disable': + $ref: 'paths/s@{spaceid}@api@slos@{sloid}@{disable}.yaml' + '/s/{spaceId}/internal/observability/slos/_historical_summary': + $ref: 'paths/s@{spaceid}@api@slos@_historical_summary.yaml' +components: + securitySchemes: + basicAuth: + type: http + scheme: basic + apiKeyAuth: + type: apiKey + in: header + name: ApiKey +security: + - basicAuth: [] + - apiKeyAuth: [] diff --git a/x-pack/plugins/observability/docs/openapi/slo/paths/README.md b/x-pack/plugins/observability/docs/openapi/slo/paths/README.md new file mode 100644 index 0000000000000..b7818c8474fc8 --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/paths/README.md @@ -0,0 +1,10 @@ +Paths +===== + +Each path definition for which there is a specification exists within this folder. + +These files currently use the following conventions: + +* path separator token (e.g. `@`) is included in the file name +* path parameter (e.g. `{example}`) is included in the file name +* there is one file per path; each file can contain multiple operations diff --git a/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos.yaml b/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos.yaml new file mode 100644 index 0000000000000..76ac5912056f1 --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos.yaml @@ -0,0 +1,113 @@ +post: + summary: Creates an SLO. + operationId: createSlo + description: > + You must have `all` privileges for the **SLOs** feature in the + **Observability** section of the Kibana feature privileges. + tags: + - slos + parameters: + - $ref: ../components/headers/kbn_xsrf.yaml + - $ref: ../components/parameters/space_id.yaml + requestBody: + required: true + content: + application/json: + schema: + $ref: '../components/schemas/create_slo_request.yaml' + responses: + '200': + description: Successful request + content: + application/json: + schema: + $ref: '../components/schemas/create_slo_response.yaml' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '../components/schemas/4xx_response.yaml' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '../components/schemas/4xx_response.yaml' + servers: + - url: https://localhost:5601 + +get: + summary: Retrieves a paginated list of SLOs + operationId: findSlos + description: > + You must have the `read` privileges for the **SLOs** feature in the + **Observability** section of the Kibana feature privileges. + tags: + - slos + parameters: + - $ref: ../components/headers/kbn_xsrf.yaml + - $ref: ../components/parameters/space_id.yaml + - name: name + in: query + description: Filter by name + schema: + type: string + example: awesome-service + - name: indicatorTypes + in: query + description: Filter by indicator type + schema: + type: array + items: + type: string + example: ["sli.kql.custom"] + - name: page + in: query + description: The page number to return + schema: + type: integer + default: 1 + example: 1 + - name: perPage + in: query + description: The number of SLOs to return per page + schema: + type: integer + default: 25 + example: 20 + - name: sortBy + in: query + description: Sort by field + schema: + type: string + enum: [name, indicatorType] + default: name + example: name + - name: sortDirection + in: query + description: Sort order + schema: + type: string + enum: [asc, desc] + default: asc + example: asc + responses: + '200': + description: Successful request + content: + application/json: + schema: + $ref: '../components/schemas/find_slo_response.yaml' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '../components/schemas/4xx_response.yaml' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '../components/schemas/4xx_response.yaml' \ No newline at end of file diff --git a/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos@_historical_summary.yaml b/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos@_historical_summary.yaml new file mode 100644 index 0000000000000..59d9cedada877 --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos@_historical_summary.yaml @@ -0,0 +1,36 @@ +post: + summary: Retrieves the historical summary for a list of SLOs + operationId: historicalSummary + description: > + You must have the `read` privileges for the **SLOs** feature in the + **Observability** section of the Kibana feature privileges. + tags: + - slos + parameters: + - $ref: ../components/headers/kbn_xsrf.yaml + - $ref: ../components/parameters/space_id.yaml + requestBody: + required: true + content: + application/json: + schema: + $ref: '../components/schemas/historical_summary_request.yaml' + responses: + '200': + description: Successful request + content: + application/json: + schema: + $ref: '../components/schemas/historical_summary_response.yaml' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '../components/schemas/4xx_response.yaml' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '../components/schemas/4xx_response.yaml' diff --git a/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos@{sloid}.yaml b/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos@{sloid}.yaml new file mode 100644 index 0000000000000..c4324108f6d6b --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos@{sloid}.yaml @@ -0,0 +1,97 @@ +get: + summary: Retrieves a SLO + operationId: getSlo + description: > + You must have the `read` privileges for the **SLOs** feature in the + **Observability** section of the Kibana feature privileges. + tags: + - slos + parameters: + - $ref: ../components/headers/kbn_xsrf.yaml + - $ref: ../components/parameters/space_id.yaml + - $ref: ../components/parameters/slo_id.yaml + responses: + '200': + description: Successful request + content: + application/json: + schema: + $ref: '../components/schemas/slo_response.yaml' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '../components/schemas/4xx_response.yaml' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '../components/schemas/4xx_response.yaml' + +put: + summary: Updates an SLO + operationId: updateSlo + description: > + You must have the `write` privileges for the **SLOs** feature in the + **Observability** section of the Kibana feature privileges. + tags: + - slos + parameters: + - $ref: ../components/headers/kbn_xsrf.yaml + - $ref: ../components/parameters/space_id.yaml + - $ref: ../components/parameters/slo_id.yaml + requestBody: + required: true + content: + application/json: + schema: + $ref: '../components/schemas/update_slo_request.yaml' + responses: + '200': + description: Successful request + content: + application/json: + schema: + $ref: '../components/schemas/slo_response.yaml' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '../components/schemas/4xx_response.yaml' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '../components/schemas/4xx_response.yaml' + +delete: + summary: Deletes an SLO + operationId: deleteSlo + description: > + You must have the `write` privileges for the **SLOs** feature in the + **Observability** section of the Kibana feature privileges. + tags: + - slos + parameters: + - $ref: ../components/headers/kbn_xsrf.yaml + - $ref: ../components/parameters/space_id.yaml + - $ref: ../components/parameters/slo_id.yaml + responses: + '204': + description: Successful request + '400': + description: Bad request + content: + application/json: + schema: + $ref: '../components/schemas/4xx_response.yaml' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '../components/schemas/4xx_response.yaml' diff --git a/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos@{sloid}@{disable}.yaml b/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos@{sloid}@{disable}.yaml new file mode 100644 index 0000000000000..a35878b62b0bc --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos@{sloid}@{disable}.yaml @@ -0,0 +1,27 @@ +post: + summary: Disables an SLO + operationId: disableSlo + description: > + You must have the `write` privileges for the **SLOs** feature in the + **Observability** section of the Kibana feature privileges. + tags: + - slos + parameters: + - $ref: ../components/headers/kbn_xsrf.yaml + - $ref: ../components/parameters/space_id.yaml + - $ref: ../components/parameters/slo_id.yaml + responses: + '200': + description: Successful request + '400': + description: Bad request + content: + application/json: + schema: + $ref: '../components/schemas/4xx_response.yaml' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '../components/schemas/4xx_response.yaml' diff --git a/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos@{sloid}@{enable}.yaml b/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos@{sloid}@{enable}.yaml new file mode 100644 index 0000000000000..bd07103f52706 --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos@{sloid}@{enable}.yaml @@ -0,0 +1,27 @@ +post: + summary: Enables an SLO + operationId: enableSlo + description: > + You must have the `write` privileges for the **SLOs** feature in the + **Observability** section of the Kibana feature privileges. + tags: + - slos + parameters: + - $ref: ../components/headers/kbn_xsrf.yaml + - $ref: ../components/parameters/space_id.yaml + - $ref: ../components/parameters/slo_id.yaml + responses: + '204': + description: Successful request + '400': + description: Bad request + content: + application/json: + schema: + $ref: '../components/schemas/4xx_response.yaml' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '../components/schemas/4xx_response.yaml' diff --git a/x-pack/plugins/observability/e2e/journeys/step_duration.journey.ts b/x-pack/plugins/observability/e2e/journeys/step_duration.journey.ts index 1e24fbb120f53..13cde8e940e13 100644 --- a/x-pack/plugins/observability/e2e/journeys/step_duration.journey.ts +++ b/x-pack/plugins/observability/e2e/journeys/step_duration.journey.ts @@ -56,10 +56,7 @@ journey('Exploratory view', async ({ page, params }) => { }); step('Open exploratory view with monitor duration', async () => { - await Promise.all([ - page.waitForNavigation(TIMEOUT_60_SEC), - page.click('text=Explore data', TIMEOUT_60_SEC), - ]); + await page.waitForNavigation(TIMEOUT_60_SEC); await waitForLoadingToFinish({ page }); await page.click('text=browser', TIMEOUT_60_SEC); diff --git a/x-pack/plugins/observability/public/components/app/burn_rate_rule_editor/burn_rate_rule_editor.stories.tsx b/x-pack/plugins/observability/public/components/app/burn_rate_rule_editor/burn_rate_rule_editor.stories.tsx index 95ec0d73aa6ae..0c53fae9af248 100644 --- a/x-pack/plugins/observability/public/components/app/burn_rate_rule_editor/burn_rate_rule_editor.stories.tsx +++ b/x-pack/plugins/observability/public/components/app/burn_rate_rule_editor/burn_rate_rule_editor.stories.tsx @@ -8,13 +8,14 @@ import React from 'react'; import { ComponentStory } from '@storybook/react'; +import { KibanaReactStorybookDecorator } from '../../../utils/kibana_react.storybook_decorator'; import { BurnRateRuleParams } from '../../../typings'; import { BurnRateRuleEditor as Component } from './burn_rate_rule_editor'; export default { component: Component, title: 'app/SLO/BurnRateRule', - argTypes: {}, + decorators: [KibanaReactStorybookDecorator], }; const Template: ComponentStory = () => ( diff --git a/x-pack/plugins/observability/public/components/app/burn_rate_rule_editor/slo_selector.stories.tsx b/x-pack/plugins/observability/public/components/app/burn_rate_rule_editor/slo_selector.stories.tsx index fd06b1fef6ab5..1debffc408049 100644 --- a/x-pack/plugins/observability/public/components/app/burn_rate_rule_editor/slo_selector.stories.tsx +++ b/x-pack/plugins/observability/public/components/app/burn_rate_rule_editor/slo_selector.stories.tsx @@ -9,11 +9,13 @@ import React from 'react'; import { ComponentStory } from '@storybook/react'; import { SLOResponse } from '@kbn/slo-schema'; +import { KibanaReactStorybookDecorator } from '../../../utils/kibana_react.storybook_decorator'; import { SloSelector as Component } from './slo_selector'; export default { component: Component, title: 'app/SLO/BurnRateRule', + decorators: [KibanaReactStorybookDecorator], }; const Template: ComponentStory = () => ( diff --git a/x-pack/plugins/observability/public/components/shared/alert_search_bar/alert_search_bar.tsx b/x-pack/plugins/observability/public/components/shared/alert_search_bar/alert_search_bar.tsx index 4d154504ce6ea..aec17127b8dd9 100644 --- a/x-pack/plugins/observability/public/components/shared/alert_search_bar/alert_search_bar.tsx +++ b/x-pack/plugins/observability/public/components/shared/alert_search_bar/alert_search_bar.tsx @@ -62,7 +62,7 @@ export function ObservabilityAlertSearchBar({ const onSearchBarParamsChange = useCallback< (query: { dateRange: { from: string; to: string; mode?: 'absolute' | 'relative' }; - query: string; + query?: string; }) => void >( ({ dateRange, query }) => { @@ -76,7 +76,7 @@ export function ObservabilityAlertSearchBar({ query, [...getAlertStatusQuery(status), ...defaultSearchQueries] ); - onKueryChange(query); + if (query) onKueryChange(query); timeFilterService.setTime(dateRange); onRangeFromChange(dateRange.from); onRangeToChange(dateRange.to); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/index.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/index.tsx index fd77820865d47..6647d2499159c 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/index.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/index.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useCallback, useMemo, useState } from 'react'; +import React, { useCallback, useMemo, useState, useEffect } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui'; import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; import type { CoreStart } from '@kbn/core/public'; @@ -55,9 +55,12 @@ export function getExploratoryViewEmbeddable( }; return (props: ExploratoryEmbeddableProps) => { - if (!services.data.search.session.getSessionId()) { - services.data.search.session.start(); - } + useEffect(() => { + if (!services.data.search.session.getSessionId()) { + services.data.search.session.start(); + } + }, []); + const { dataTypesIndexPatterns, attributes, customHeight } = props; if (!dataViewsService || !lens || !attributes || attributes?.length === 0) { diff --git a/x-pack/plugins/upgrade_assistant/server/saved_object_types/migrations/index.ts b/x-pack/plugins/observability/public/hooks/slo/__storybook_mocks__/use_capabilities.ts similarity index 68% rename from x-pack/plugins/upgrade_assistant/server/saved_object_types/migrations/index.ts rename to x-pack/plugins/observability/public/hooks/slo/__storybook_mocks__/use_capabilities.ts index 5e6e379bd9b2b..f34d7a4a7070b 100644 --- a/x-pack/plugins/upgrade_assistant/server/saved_object_types/migrations/index.ts +++ b/x-pack/plugins/observability/public/hooks/slo/__storybook_mocks__/use_capabilities.ts @@ -5,4 +5,9 @@ * 2.0. */ -export { telemetrySavedObjectMigrations } from './telemetry_saved_object_migrations'; +export function useCapabilities() { + return { + hasReadCapabilities: true, + hasWriteCapabilities: true, + }; +} diff --git a/x-pack/plugins/observability/public/hooks/slo/__storybook_mocks__/use_fetch_active_alerts.ts b/x-pack/plugins/observability/public/hooks/slo/__storybook_mocks__/use_fetch_active_alerts.ts new file mode 100644 index 0000000000000..f493eadac3806 --- /dev/null +++ b/x-pack/plugins/observability/public/hooks/slo/__storybook_mocks__/use_fetch_active_alerts.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 { UseFetchActiveAlerts } from '../use_fetch_active_alerts'; + +export const useFetchActiveAlerts = ({ + sloIds = [], +}: { + sloIds: string[]; +}): UseFetchActiveAlerts => { + return { + isLoading: false, + isSuccess: false, + isError: false, + data: sloIds.reduce( + (acc, sloId, index) => ({ + ...acc, + ...(index % 2 === 0 && { [sloId]: { count: 2, ruleIds: ['rule-1', 'rule-2'] } }), + }), + {} + ), + }; +}; diff --git a/x-pack/plugins/observability/public/hooks/slo/__storybook_mocks__/use_fetch_apm_suggestions.ts b/x-pack/plugins/observability/public/hooks/slo/__storybook_mocks__/use_fetch_apm_suggestions.ts index 4edea41bdc1fb..d14143fe9ae0e 100644 --- a/x-pack/plugins/observability/public/hooks/slo/__storybook_mocks__/use_fetch_apm_suggestions.ts +++ b/x-pack/plugins/observability/public/hooks/slo/__storybook_mocks__/use_fetch_apm_suggestions.ts @@ -10,12 +10,12 @@ import { Params, UseFetchApmSuggestions } from '../use_fetch_apm_suggestions'; export const useFetchApmSuggestions = ({ fieldName, search = '', + serviceName = '', }: Params): UseFetchApmSuggestions => { return { isLoading: false, isError: false, isSuccess: true, suggestions: ['apm-suggestion-1', 'apm-suggestion-2', 'apm-suggestion-3'], - refetch: function () {} as UseFetchApmSuggestions['refetch'], }; }; diff --git a/x-pack/plugins/observability/public/hooks/slo/__storybook_mocks__/use_fetch_historical_summary.ts b/x-pack/plugins/observability/public/hooks/slo/__storybook_mocks__/use_fetch_historical_summary.ts index f869e6da47c64..fe10d84c4f428 100644 --- a/x-pack/plugins/observability/public/hooks/slo/__storybook_mocks__/use_fetch_historical_summary.ts +++ b/x-pack/plugins/observability/public/hooks/slo/__storybook_mocks__/use_fetch_historical_summary.ts @@ -23,6 +23,5 @@ export const useFetchHistoricalSummary = ({ isSuccess: false, isError: false, sloHistoricalSummaryResponse: data, - refetch: function () {} as UseFetchHistoricalSummaryResponse['refetch'], }; }; diff --git a/x-pack/plugins/observability/public/hooks/slo/use_fetch_active_alerts.ts b/x-pack/plugins/observability/public/hooks/slo/use_fetch_active_alerts.ts new file mode 100644 index 0000000000000..4e90e51060df0 --- /dev/null +++ b/x-pack/plugins/observability/public/hooks/slo/use_fetch_active_alerts.ts @@ -0,0 +1,115 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useQuery } from '@tanstack/react-query'; +import { BASE_RAC_ALERTS_API_PATH } from '@kbn/rule-registry-plugin/common'; + +import { useKibana } from '../../utils/kibana_react'; + +type SloId = string; + +interface Params { + sloIds: SloId[]; +} + +export interface ActiveAlerts { + count: number; + ruleIds: string[]; +} + +type ActiveAlertsMap = Record; + +export interface UseFetchActiveAlerts { + data: ActiveAlertsMap; + isLoading: boolean; + isSuccess: boolean; + isError: boolean; +} + +interface FindApiResponse { + aggregations: { + perSloId: { + buckets: Array<{ + key: string; + doc_count: number; + perRuleId: { buckets: Array<{ key: string; doc_count: number }> }; + }>; + }; + }; +} + +const EMPTY_ACTIVE_ALERTS_MAP = {}; + +export function useFetchActiveAlerts({ sloIds = [] }: Params): UseFetchActiveAlerts { + const { http } = useKibana().services; + + const { isInitialLoading, isLoading, isError, isSuccess, isRefetching, data } = useQuery({ + queryKey: ['fetchActiveAlerts', sloIds], + queryFn: async ({ signal }) => { + try { + const response = await http.post(`${BASE_RAC_ALERTS_API_PATH}/find`, { + body: JSON.stringify({ + feature_ids: ['slo'], + size: 0, + query: { + bool: { + filter: [ + { + term: { + 'kibana.alert.rule.rule_type_id': 'slo.rules.burnRate', + }, + }, + { + term: { + 'kibana.alert.status': 'active', + }, + }, + ], + }, + }, + aggs: { + perSloId: { + terms: { + field: 'kibana.alert.rule.parameters.sloId', + }, + aggs: { + perRuleId: { + terms: { + field: 'kibana.alert.rule.uuid', + }, + }, + }, + }, + }, + }), + signal, + }); + + return response.aggregations.perSloId.buckets.reduce( + (acc, bucket) => ({ + ...acc, + [bucket.key]: { + count: bucket.doc_count ?? 0, + ruleIds: bucket.perRuleId.buckets.map((rule) => rule.key), + } as ActiveAlerts, + }), + {} + ); + } catch (error) { + // ignore error + } + }, + refetchOnWindowFocus: false, + }); + + return { + data: isInitialLoading ? EMPTY_ACTIVE_ALERTS_MAP : data ?? EMPTY_ACTIVE_ALERTS_MAP, + isLoading: isInitialLoading || isLoading || isRefetching, + isSuccess, + isError, + }; +} diff --git a/x-pack/plugins/observability/public/hooks/slo/use_fetch_apm_indices.ts b/x-pack/plugins/observability/public/hooks/slo/use_fetch_apm_indices.ts new file mode 100644 index 0000000000000..702573fa570dd --- /dev/null +++ b/x-pack/plugins/observability/public/hooks/slo/use_fetch_apm_indices.ts @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useQuery } from '@tanstack/react-query'; + +import { useKibana } from '../../utils/kibana_react'; + +type ApmIndex = string; + +export interface UseFetchApmIndex { + data: ApmIndex; + isLoading: boolean; + isSuccess: boolean; + isError: boolean; +} + +interface ApiResponse { + apmIndexSettings: Array<{ + configurationName: string; + defaultValue: string; + savedValue?: string; + }>; +} + +export function useFetchApmIndex(): UseFetchApmIndex { + const { http } = useKibana().services; + + const { isInitialLoading, isLoading, isError, isSuccess, isRefetching, data } = useQuery({ + queryKey: ['fetchApmIndices'], + queryFn: async ({ signal }) => { + try { + const response = await http.get('/internal/apm/settings/apm-index-settings', { + signal, + }); + + const metricSettings = response.apmIndexSettings.find( + (settings) => settings.configurationName === 'metric' + ); + + let index = ''; + if (!!metricSettings) { + index = metricSettings.savedValue ?? metricSettings.defaultValue; + } + + return index; + } catch (error) { + // ignore error + } + }, + refetchOnWindowFocus: false, + }); + + return { + data: isInitialLoading ? '' : data ?? '', + isLoading: isInitialLoading || isLoading || isRefetching, + isSuccess, + isError, + }; +} diff --git a/x-pack/plugins/observability/public/hooks/slo/use_fetch_apm_suggestions.ts b/x-pack/plugins/observability/public/hooks/slo/use_fetch_apm_suggestions.ts index 72350d9227867..63ed913902ee8 100644 --- a/x-pack/plugins/observability/public/hooks/slo/use_fetch_apm_suggestions.ts +++ b/x-pack/plugins/observability/public/hooks/slo/use_fetch_apm_suggestions.ts @@ -5,68 +5,66 @@ * 2.0. */ -import { - QueryObserverResult, - RefetchOptions, - RefetchQueryFilters, - useQuery, -} from '@tanstack/react-query'; +import { useQuery } from '@tanstack/react-query'; import moment from 'moment'; + import { useKibana } from '../../utils/kibana_react'; export type Suggestion = string; + export interface UseFetchApmSuggestions { suggestions: Suggestion[]; isLoading: boolean; isSuccess: boolean; isError: boolean; - refetch: ( - options?: (RefetchOptions & RefetchQueryFilters) | undefined - ) => Promise>; } export interface Params { fieldName: string; - search: string; + search?: string; + serviceName?: string; } interface ApiResponse { terms: string[]; } -const EMPTY_RESPONSE: ApiResponse = { terms: [] }; +const NO_SUGGESTIONS: Suggestion[] = []; -export function useFetchApmSuggestions({ fieldName, search = '' }: Params): UseFetchApmSuggestions { +export function useFetchApmSuggestions({ + fieldName, + search = '', + serviceName = '', +}: Params): UseFetchApmSuggestions { const { http } = useKibana().services; - const { isInitialLoading, isLoading, isError, isSuccess, isRefetching, data, refetch } = useQuery( - { - queryKey: ['fetchApmSuggestions', fieldName, search], - queryFn: async ({ signal }) => { - try { - const { terms = [] } = await http.get('/internal/apm/suggestions', { - query: { - fieldName, - start: moment().subtract(2, 'days').toISOString(), - end: moment().toISOString(), - fieldValue: search, - }, - signal, - }); + const { isInitialLoading, isLoading, isError, isSuccess, isRefetching, data } = useQuery({ + queryKey: ['fetchApmSuggestions', fieldName, search, serviceName], + queryFn: async ({ signal }) => { + try { + const { terms = [] } = await http.get('/internal/apm/suggestions', { + query: { + fieldName, + start: moment().subtract(2, 'days').toISOString(), + end: moment().toISOString(), + fieldValue: search, + ...(!!serviceName && { serviceName }), + }, + signal, + }); - return terms; - } catch (error) { - // ignore error for retrieving slos - } - }, - } - ); + return terms; + } catch (error) { + // ignore error + } + }, + refetchOnWindowFocus: false, + }); return { - suggestions: isInitialLoading ? EMPTY_RESPONSE.terms : data ?? EMPTY_RESPONSE.terms, + suggestions: isInitialLoading ? NO_SUGGESTIONS : data ?? NO_SUGGESTIONS, isLoading: isInitialLoading || isLoading || isRefetching, isSuccess, isError, - refetch, }; } diff --git a/x-pack/plugins/observability/public/hooks/slo/use_fetch_historical_summary.ts b/x-pack/plugins/observability/public/hooks/slo/use_fetch_historical_summary.ts index e1bca23188295..2037c97df52c1 100644 --- a/x-pack/plugins/observability/public/hooks/slo/use_fetch_historical_summary.ts +++ b/x-pack/plugins/observability/public/hooks/slo/use_fetch_historical_summary.ts @@ -5,12 +5,7 @@ * 2.0. */ -import { - QueryObserverResult, - RefetchOptions, - RefetchQueryFilters, - useQuery, -} from '@tanstack/react-query'; +import { useQuery } from '@tanstack/react-query'; import { FetchHistoricalSummaryResponse } from '@kbn/slo-schema'; import { useKibana } from '../../utils/kibana_react'; @@ -22,9 +17,6 @@ export interface UseFetchHistoricalSummaryResponse { isLoading: boolean; isSuccess: boolean; isError: boolean; - refetch: ( - options?: (RefetchOptions & RefetchQueryFilters) | undefined - ) => Promise>; } export interface Params { @@ -36,33 +28,30 @@ export function useFetchHistoricalSummary({ }: Params): UseFetchHistoricalSummaryResponse { const { http } = useKibana().services; - const { isInitialLoading, isLoading, isError, isSuccess, isRefetching, data, refetch } = useQuery( - { - queryKey: ['fetchHistoricalSummary', sloIds], - queryFn: async ({ signal }) => { - try { - const response = await http.post( - '/internal/observability/slos/_historical_summary', - { - body: JSON.stringify({ sloIds }), - signal, - } - ); - - return response; - } catch (error) { - // ignore error for retrieving slos - } - }, - refetchOnWindowFocus: false, - } - ); + const { isInitialLoading, isLoading, isError, isSuccess, isRefetching, data } = useQuery({ + queryKey: ['fetchHistoricalSummary', sloIds], + queryFn: async ({ signal }) => { + try { + const response = await http.post( + '/internal/observability/slos/_historical_summary', + { + body: JSON.stringify({ sloIds }), + signal, + } + ); + + return response; + } catch (error) { + // ignore error + } + }, + refetchOnWindowFocus: false, + }); return { sloHistoricalSummaryResponse: isInitialLoading ? EMPTY_RESPONSE : data ?? EMPTY_RESPONSE, isLoading: isInitialLoading || isLoading || isRefetching, isSuccess, isError, - refetch, }; } diff --git a/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_details.ts b/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_details.ts index e91269d4b1d9b..b706add340801 100644 --- a/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_details.ts +++ b/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_details.ts @@ -43,6 +43,7 @@ export function useFetchSloDetails(sloId: string): UseFetchSloDetailsResponse { } }, enabled: Boolean(sloId), + refetchOnWindowFocus: false, } ); diff --git a/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_list.ts b/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_list.ts index 7864b85181a3f..648efb5481d31 100644 --- a/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_list.ts +++ b/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_list.ts @@ -11,8 +11,8 @@ import { RefetchQueryFilters, useQuery, } from '@tanstack/react-query'; - import { FindSLOResponse } from '@kbn/slo-schema'; + import { useKibana } from '../../utils/kibana_react'; interface SLOListParams { @@ -61,7 +61,7 @@ export function useFetchSloList({ return response; } catch (error) { - // ignore error for retrieving slos + // ignore error } }, refetchOnWindowFocus: false, diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/apm_availability/apm_availability_indicator_type_form.stories.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_availability/apm_availability_indicator_type_form.stories.tsx index 9f1ca20c3411f..c3c506eb484eb 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/apm_availability/apm_availability_indicator_type_form.stories.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_availability/apm_availability_indicator_type_form.stories.tsx @@ -10,10 +10,7 @@ import { ComponentStory } from '@storybook/react'; import { FormProvider, useForm } from 'react-hook-form'; import { KibanaReactStorybookDecorator } from '../../../../utils/kibana_react.storybook_decorator'; -import { - ApmAvailabilityIndicatorTypeForm as Component, - Props, -} from './apm_availability_indicator_type_form'; +import { ApmAvailabilityIndicatorTypeForm as Component } from './apm_availability_indicator_type_form'; import { SLO_EDIT_FORM_DEFAULT_VALUES } from '../../constants'; export default { @@ -22,11 +19,11 @@ export default { decorators: [KibanaReactStorybookDecorator], }; -const Template: ComponentStory = (props: Props) => { +const Template: ComponentStory = () => { const methods = useForm({ defaultValues: SLO_EDIT_FORM_DEFAULT_VALUES }); return ( - + ); }; diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/apm_availability/apm_availability_indicator_type_form.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_availability/apm_availability_indicator_type_form.tsx index bac23feb17b85..e61e1c7056596 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/apm_availability/apm_availability_indicator_type_form.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_availability/apm_availability_indicator_type_form.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React from 'react'; +import React, { useEffect } from 'react'; import { EuiComboBox, EuiComboBoxOptionOption, @@ -13,17 +13,21 @@ import { EuiFlexItem, EuiFormLabel, } from '@elastic/eui'; -import { Control, Controller } from 'react-hook-form'; +import { Controller, useFormContext } from 'react-hook-form'; import { i18n } from '@kbn/i18n'; import type { CreateSLOInput } from '@kbn/slo-schema'; -import { FieldSelector } from '../common/field_selector'; +import { useFetchApmIndex } from '../../../../hooks/slo/use_fetch_apm_indices'; +import { FieldSelector } from '../apm_common/field_selector'; +import { QueryBuilder } from '../common/query_builder'; -export interface Props { - control: Control; -} +export function ApmAvailabilityIndicatorTypeForm() { + const { control, setValue, watch } = useFormContext(); + const { data: apmIndex } = useFetchApmIndex(); + useEffect(() => { + setValue('indicator.params.index', apmIndex); + }, [apmIndex, setValue]); -export function ApmAvailabilityIndicatorTypeForm({ control }: Props) { return ( @@ -40,7 +44,6 @@ export function ApmAvailabilityIndicatorTypeForm({ control }: Props) { )} fieldName="service.name" name="indicator.params.service" - control={control} dataTestSubj="apmAvailabilityServiceSelector" /> @@ -79,7 +81,6 @@ export function ApmAvailabilityIndicatorTypeForm({ control }: Props) { )} fieldName="transaction.type" name="indicator.params.transactionType" - control={control} dataTestSubj="apmAvailabilityTransactionTypeSelector" /> @@ -131,10 +131,14 @@ export function ApmAvailabilityIndicatorTypeForm({ control }: Props) { } )} isInvalid={!!fieldState.error} - options={generateStatusCodeOptions()} + options={generateStatusCodeOptions(['2xx', '3xx', '4xx', '5xx'])} selectedOptions={generateStatusCodeOptions(field.value)} onChange={(selected: EuiComboBoxOptionOption[]) => { - field.onChange(selected.map((opts) => opts.value)); + if (selected.length) { + return field.onChange(selected.map((opts) => opts.value)); + } + + field.onChange([]); }} isClearable={true} data-test-subj="sloEditApmAvailabilityGoodStatusCodesSelector" @@ -142,13 +146,29 @@ export function ApmAvailabilityIndicatorTypeForm({ control }: Props) { )} /> - + + + ); } -function generateStatusCodeOptions(codes: string[] = ['2xx', '3xx', '4xx', '5xx']) { +function generateStatusCodeOptions(codes: string[] = []) { return codes.map((code) => ({ label: code, value: code, diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/common/field_selector.stories.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_common/field_selector.stories.tsx similarity index 91% rename from x-pack/plugins/observability/public/pages/slo_edit/components/common/field_selector.stories.tsx rename to x-pack/plugins/observability/public/pages/slo_edit/components/apm_common/field_selector.stories.tsx index b20915109f2b4..47111ee1c565b 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/common/field_selector.stories.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_common/field_selector.stories.tsx @@ -15,7 +15,7 @@ import { SLO_EDIT_FORM_DEFAULT_VALUES } from '../../constants'; export default { component: Component, - title: 'app/SLO/EditPage/Common/FieldSelector', + title: 'app/SLO/EditPage/ApmCommon/FieldSelector', decorators: [KibanaReactStorybookDecorator], }; @@ -23,7 +23,7 @@ const Template: ComponentStory = (props: Props) => { const methods = useForm({ defaultValues: SLO_EDIT_FORM_DEFAULT_VALUES }); return ( - + ); }; diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/common/field_selector.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_common/field_selector.tsx similarity index 79% rename from x-pack/plugins/observability/public/pages/slo_edit/components/common/field_selector.tsx rename to x-pack/plugins/observability/public/pages/slo_edit/components/apm_common/field_selector.tsx index 665758c63008e..92dc0ba682225 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/common/field_selector.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_common/field_selector.tsx @@ -5,9 +5,9 @@ * 2.0. */ -import React, { useEffect, useState } from 'react'; +import React, { useState } from 'react'; import { EuiComboBox, EuiComboBoxOptionOption, EuiFlexItem, EuiFormLabel } from '@elastic/eui'; -import { Control, Controller, FieldPath } from 'react-hook-form'; +import { Controller, FieldPath, useFormContext } from 'react-hook-form'; import { CreateSLOInput } from '@kbn/slo-schema'; import { i18n } from '@kbn/i18n'; import { @@ -22,7 +22,6 @@ interface Option { export interface Props { allowAllOption?: boolean; - control: Control; dataTestSubj: string; fieldName: string; label: string; @@ -32,37 +31,33 @@ export interface Props { export function FieldSelector({ allowAllOption = true, - control, dataTestSubj, fieldName, label, name, placeholder, }: Props) { + const { control, watch } = useFormContext(); + const serviceName = watch('indicator.params.service'); const [search, setSearch] = useState(''); const { suggestions, isLoading } = useFetchApmSuggestions({ fieldName, search, + serviceName, }); - const [options, setOptions] = useState([]); - useEffect(() => { - const opts = ( - allowAllOption - ? [ - { - value: '*', - label: i18n.translate('xpack.observability.slos.sloEdit.fieldSelector.all', { - defaultMessage: 'All', - }), - }, - ] - : [] - ).concat(createOptions(suggestions)); - - setOptions(opts); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [suggestions.length]); + const options = ( + allowAllOption + ? [ + { + value: '*', + label: i18n.translate('xpack.observability.slos.sloEdit.fieldSelector.all', { + defaultMessage: 'All', + }), + }, + ] + : [] + ).concat(createOptions(suggestions)); return ( @@ -81,6 +76,7 @@ export function FieldSelector({ async data-test-subj={dataTestSubj} isClearable={true} + isDisabled={name !== 'indicator.params.service' && !serviceName} isInvalid={!!fieldState.error} isLoading={isLoading} onChange={(selected: EuiComboBoxOptionOption[]) => { diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.stories.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.stories.tsx index 7b98c21b6e5e4..3ca02641f9bfa 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.stories.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.stories.tsx @@ -10,7 +10,7 @@ import { ComponentStory } from '@storybook/react'; import { FormProvider, useForm } from 'react-hook-form'; import { KibanaReactStorybookDecorator } from '../../../../utils/kibana_react.storybook_decorator'; -import { ApmLatencyIndicatorTypeForm as Component, Props } from './apm_latency_indicator_type_form'; +import { ApmLatencyIndicatorTypeForm as Component } from './apm_latency_indicator_type_form'; import { SLO_EDIT_FORM_DEFAULT_VALUES } from '../../constants'; export default { @@ -19,11 +19,11 @@ export default { decorators: [KibanaReactStorybookDecorator], }; -const Template: ComponentStory = (props: Props) => { +const Template: ComponentStory = () => { const methods = useForm({ defaultValues: SLO_EDIT_FORM_DEFAULT_VALUES }); return ( - + ); }; diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.tsx index a8515d157bbcf..a5f39a0f8f958 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.tsx @@ -5,19 +5,23 @@ * 2.0. */ -import React from 'react'; +import React, { useEffect } from 'react'; import { EuiFieldNumber, EuiFlexGroup, EuiFlexItem, EuiFormLabel } from '@elastic/eui'; -import { Control, Controller } from 'react-hook-form'; +import { Controller, useFormContext } from 'react-hook-form'; import { i18n } from '@kbn/i18n'; import type { CreateSLOInput } from '@kbn/slo-schema'; -import { FieldSelector } from '../common/field_selector'; +import { useFetchApmIndex } from '../../../../hooks/slo/use_fetch_apm_indices'; +import { FieldSelector } from '../apm_common/field_selector'; +import { QueryBuilder } from '../common/query_builder'; -export interface Props { - control: Control; -} +export function ApmLatencyIndicatorTypeForm() { + const { control, setValue, watch } = useFormContext(); + const { data: apmIndex } = useFetchApmIndex(); + useEffect(() => { + setValue('indicator.params.index', apmIndex); + }, [apmIndex, setValue]); -export function ApmLatencyIndicatorTypeForm({ control }: Props) { return ( @@ -34,7 +38,6 @@ export function ApmLatencyIndicatorTypeForm({ control }: Props) { )} fieldName="service.name" name="indicator.params.service" - control={control} dataTestSubj="apmLatencyServiceSelector" /> @@ -67,7 +69,6 @@ export function ApmLatencyIndicatorTypeForm({ control }: Props) { )} fieldName="transaction.type" name="indicator.params.transactionType" - control={control} dataTestSubj="apmLatencyTransactionTypeSelector" /> @@ -114,7 +114,23 @@ export function ApmLatencyIndicatorTypeForm({ control }: Props) { )} /> - + + + ); diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/query_builder.stories.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/common/query_builder.stories.tsx similarity index 100% rename from x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/query_builder.stories.tsx rename to x-pack/plugins/observability/public/pages/slo_edit/components/common/query_builder.stories.tsx diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/query_builder.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/common/query_builder.tsx similarity index 100% rename from x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/query_builder.tsx rename to x-pack/plugins/observability/public/pages/slo_edit/components/common/query_builder.tsx diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.stories.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.stories.tsx index 375b133adfb43..5eb0b68070789 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.stories.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.stories.tsx @@ -10,7 +10,7 @@ import { ComponentStory } from '@storybook/react'; import { FormProvider, useForm } from 'react-hook-form'; import { KibanaReactStorybookDecorator } from '../../../../utils/kibana_react.storybook_decorator'; -import { CustomKqlIndicatorTypeForm as Component, Props } from './custom_kql_indicator_type_form'; +import { CustomKqlIndicatorTypeForm as Component } from './custom_kql_indicator_type_form'; import { SLO_EDIT_FORM_DEFAULT_VALUES } from '../../constants'; export default { @@ -19,11 +19,11 @@ export default { decorators: [KibanaReactStorybookDecorator], }; -const Template: ComponentStory = (props: Props) => { +const Template: ComponentStory = () => { const methods = useForm({ defaultValues: SLO_EDIT_FORM_DEFAULT_VALUES }); return ( - + ); }; diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.tsx index 07b8bf4d192eb..a9fb7cd58e480 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.tsx @@ -8,18 +8,14 @@ import React from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { Control, UseFormWatch } from 'react-hook-form'; -import type { CreateSLOInput } from '@kbn/slo-schema'; +import { useFormContext } from 'react-hook-form'; +import { CreateSLOInput } from '@kbn/slo-schema'; import { IndexSelection } from './index_selection'; -import { QueryBuilder } from './query_builder'; +import { QueryBuilder } from '../common/query_builder'; -export interface Props { - control: Control; - watch: UseFormWatch; -} - -export function CustomKqlIndicatorTypeForm({ control, watch }: Props) { +export function CustomKqlIndicatorTypeForm() { + const { control, watch } = useFormContext(); return ( diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form.tsx index 4728d72bdec6c..01a16fe8c8220 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form.tsx @@ -20,7 +20,7 @@ import { } from '@elastic/eui'; import { euiThemeVars } from '@kbn/ui-theme'; import { i18n } from '@kbn/i18n'; -import { Controller, useForm } from 'react-hook-form'; +import { Controller, FormProvider, useForm } from 'react-hook-form'; import type { SLOWithSummaryResponse } from '@kbn/slo-schema'; import { useKibana } from '../../../utils/kibana_react'; @@ -53,11 +53,12 @@ export function SloEditForm({ slo }: Props) { notifications: { toasts }, } = useKibana().services; - const { control, watch, getFieldState, getValues, formState } = useForm({ + const methods = useForm({ defaultValues: SLO_EDIT_FORM_DEFAULT_VALUES, values: transformSloResponseToCreateSloInput(slo), mode: 'all', }); + const { control, watch, getFieldState, getValues, formState } = methods; const { isIndicatorSectionValid, isDescriptionSectionValid, isObjectiveSectionValid } = useSectionFormValidation({ @@ -122,159 +123,165 @@ export function SloEditForm({ slo }: Props) { const getIndicatorTypeForm = () => { switch (watch('indicator.type')) { case 'sli.kql.custom': - return ; + return ; case 'sli.apm.transactionDuration': - return ; + return ; case 'sli.apm.transactionErrorRate': - return ; + return ; default: return null; } }; return ( - - - } - > - - -

    - {i18n.translate('xpack.observability.slos.sloEdit.definition.title', { - defaultMessage: 'Define SLI', + + + + } + > + + +

    + {i18n.translate('xpack.observability.slos.sloEdit.definition.title', { + defaultMessage: 'Define SLI', + })} +

    +
    + + + + + {i18n.translate('xpack.observability.slos.sloEdit.definition.sliType', { + defaultMessage: 'SLI type', })} -

    -
    - - - - - {i18n.translate('xpack.observability.slos.sloEdit.definition.sliType', { - defaultMessage: 'SLI type', - })} - - - ( - - )} - /> - - - - {getIndicatorTypeForm()} - - -
    -
    - - - } - verticalAlign="top" - > - - -

    - {i18n.translate('xpack.observability.slos.sloEdit.objectives.title', { - defaultMessage: 'Set objectives', - })} -

    -
    - - - - - - -
    -
    - - - } - > - - -

    - {i18n.translate('xpack.observability.slos.sloEdit.description.title', { - defaultMessage: 'Describe SLO', - })} -

    -
    - - - - - - - - - - {isEditMode - ? i18n.translate('xpack.observability.slos.sloEdit.editSloButton', { - defaultMessage: 'Update SLO', - }) - : i18n.translate('xpack.observability.slos.sloEdit.createSloButton', { - defaultMessage: 'Create SLO', - })} - - - navigateToUrl(basePath.prepend(paths.observability.slos))} - > - {i18n.translate('xpack.observability.slos.sloEdit.cancelButton', { - defaultMessage: 'Cancel', - })} - - - - -
    -
    -
    + + + ( + + )} + /> + + + + {getIndicatorTypeForm()} + + + + + + + } + verticalAlign="top" + > + + +

    + {i18n.translate('xpack.observability.slos.sloEdit.objectives.title', { + defaultMessage: 'Set objectives', + })} +

    +
    + + + + + + +
    +
    + + + } + > + + +

    + {i18n.translate('xpack.observability.slos.sloEdit.description.title', { + defaultMessage: 'Describe SLO', + })} +

    +
    + + + + + + + + + + {isEditMode + ? i18n.translate('xpack.observability.slos.sloEdit.editSloButton', { + defaultMessage: 'Update SLO', + }) + : i18n.translate('xpack.observability.slos.sloEdit.createSloButton', { + defaultMessage: 'Create SLO', + })} + + + navigateToUrl(basePath.prepend(paths.observability.slos))} + > + {i18n.translate('xpack.observability.slos.sloEdit.cancelButton', { + defaultMessage: 'Cancel', + })} + + + + +
    +
    + + ); } diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_description.stories.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_description.stories.tsx index 42b6d674b2f6d..69b765ab6a4b9 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_description.stories.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_description.stories.tsx @@ -10,7 +10,7 @@ import { ComponentStory } from '@storybook/react'; import { FormProvider, useForm } from 'react-hook-form'; import { KibanaReactStorybookDecorator } from '../../../utils/kibana_react.storybook_decorator'; -import { SloEditFormDescription as Component, Props } from './slo_edit_form_description'; +import { SloEditFormDescription as Component } from './slo_edit_form_description'; import { SLO_EDIT_FORM_DEFAULT_VALUES } from '../constants'; export default { @@ -19,11 +19,11 @@ export default { decorators: [KibanaReactStorybookDecorator], }; -const Template: ComponentStory = (props: Props) => { +const Template: ComponentStory = () => { const methods = useForm({ defaultValues: SLO_EDIT_FORM_DEFAULT_VALUES }); return ( - + ); }; diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_description.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_description.tsx index 15e13c53862b3..6b6780358376f 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_description.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_description.tsx @@ -15,14 +15,11 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; -import { Control, Controller } from 'react-hook-form'; +import { Controller, useFormContext } from 'react-hook-form'; import type { CreateSLOInput } from '@kbn/slo-schema'; -export interface Props { - control: Control; -} - -export function SloEditFormDescription({ control }: Props) { +export function SloEditFormDescription() { + const { control } = useFormContext(); const sloNameId = useGeneratedHtmlId({ prefix: 'sloName' }); const descriptionId = useGeneratedHtmlId({ prefix: 'sloDescription' }); diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives.stories.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives.stories.tsx index 3540c71fc231d..edb9779f3ca7f 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives.stories.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives.stories.tsx @@ -10,7 +10,7 @@ import { ComponentStory } from '@storybook/react'; import { FormProvider, useForm } from 'react-hook-form'; import { KibanaReactStorybookDecorator } from '../../../utils/kibana_react.storybook_decorator'; -import { SloEditFormObjectives as Component, Props } from './slo_edit_form_objectives'; +import { SloEditFormObjectives as Component } from './slo_edit_form_objectives'; import { SLO_EDIT_FORM_DEFAULT_VALUES } from '../constants'; export default { @@ -19,11 +19,11 @@ export default { decorators: [KibanaReactStorybookDecorator], }; -const Template: ComponentStory = (props: Props) => { +const Template: ComponentStory = () => { const methods = useForm({ defaultValues: SLO_EDIT_FORM_DEFAULT_VALUES }); return ( - + ); }; diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives.tsx index 0bd10e6d0fcf5..083224af18595 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives.tsx @@ -16,18 +16,14 @@ import { useGeneratedHtmlId, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { Control, Controller, UseFormWatch } from 'react-hook-form'; +import { Controller, useFormContext } from 'react-hook-form'; import type { CreateSLOInput } from '@kbn/slo-schema'; import { SloEditFormObjectivesTimeslices } from './slo_edit_form_objectives_timeslices'; import { BUDGETING_METHOD_OPTIONS, TIMEWINDOW_OPTIONS } from '../constants'; -export interface Props { - control: Control; - watch: UseFormWatch; -} - -export function SloEditFormObjectives({ control, watch }: Props) { +export function SloEditFormObjectives() { + const { control, watch } = useFormContext(); const budgetingSelect = useGeneratedHtmlId({ prefix: 'budgetingSelect' }); const timeWindowSelect = useGeneratedHtmlId({ prefix: 'timeWindowSelect' }); @@ -112,7 +108,7 @@ export function SloEditFormObjectives({ control, watch }: Props) { {watch('budgetingMethod') === 'timeslices' ? ( <> - + ) : null} diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives_timeslices.stories.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives_timeslices.stories.tsx index 7bdd33cf741bd..74320a2bf0ef2 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives_timeslices.stories.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives_timeslices.stories.tsx @@ -10,10 +10,7 @@ import { ComponentStory } from '@storybook/react'; import { FormProvider, useForm } from 'react-hook-form'; import { KibanaReactStorybookDecorator } from '../../../utils/kibana_react.storybook_decorator'; -import { - SloEditFormObjectivesTimeslices as Component, - Props, -} from './slo_edit_form_objectives_timeslices'; +import { SloEditFormObjectivesTimeslices as Component } from './slo_edit_form_objectives_timeslices'; import { SLO_EDIT_FORM_DEFAULT_VALUES } from '../constants'; export default { @@ -22,11 +19,11 @@ export default { decorators: [KibanaReactStorybookDecorator], }; -const Template: ComponentStory = (props: Props) => { +const Template: ComponentStory = () => { const methods = useForm({ defaultValues: SLO_EDIT_FORM_DEFAULT_VALUES }); return ( - + ); }; diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives_timeslices.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives_timeslices.tsx index 971c2b5fb30ce..2d18c57c203c4 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives_timeslices.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives_timeslices.tsx @@ -8,14 +8,11 @@ import React from 'react'; import { EuiFieldNumber, EuiFlexGrid, EuiFlexItem, EuiFormLabel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { Control, Controller } from 'react-hook-form'; +import { Controller, useFormContext } from 'react-hook-form'; import type { CreateSLOInput } from '@kbn/slo-schema'; -export interface Props { - control: Control; -} - -export function SloEditFormObjectivesTimeslices({ control }: Props) { +export function SloEditFormObjectivesTimeslices() { + const { control } = useFormContext(); return ( diff --git a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_badges.tsx b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_badges.tsx index 32cdeb785270e..cd4c7d9c2d5d5 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_badges.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_badges.tsx @@ -6,20 +6,38 @@ */ import React from 'react'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { EuiBadge, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { SLOWithSummaryResponse } from '@kbn/slo-schema'; +import { i18n } from '@kbn/i18n'; +import { useKibana } from '../../../../utils/kibana_react'; +import { paths } from '../../../../config'; +import { ActiveAlerts } from '../../../../hooks/slo/use_fetch_active_alerts'; import { SloStatusBadge } from './slo_status_badge'; import { SloIndicatorTypeBadge } from './slo_indicator_type_badge'; import { SloTimeWindowBadge } from './slo_time_window_badge'; export interface Props { slo: SLOWithSummaryResponse; + activeAlerts?: ActiveAlerts; } -export function SloBadges({ slo }: Props) { +export function SloBadges({ slo, activeAlerts }: Props) { + const { + application: { navigateToUrl }, + http: { basePath }, + } = useKibana().services; + + const handleClick = () => { + if (activeAlerts) { + navigateToUrl( + `${basePath.prepend(paths.observability.alerts)}?_a=${toAlertsPageQuery(activeAlerts)}` + ); + } + }; + return ( - + @@ -27,6 +45,34 @@ export function SloBadges({ slo }: Props) { + {!!activeAlerts && ( + + + {i18n.translate('xpack.observability.slos.slo.activeAlertsBadge.label', { + defaultMessage: '{count, plural, one {# alert} other {# alerts}}', + values: { count: activeAlerts.count }, + })} + + + )} ); } + +function toAlertsPageQuery(activeAlerts: ActiveAlerts): string { + const kuery = activeAlerts.ruleIds + .map((ruleId) => `kibana.alert.rule.uuid:"${activeAlerts.ruleIds[0]}"`) + .join(' or '); + + const query = `(kuery:'${kuery}',rangeFrom:now-15m,rangeTo:now,status:all)`; + return query; +} diff --git a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_status_badge.tsx b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_status_badge.tsx index a1c89ca857b18..ed69ebae221e5 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_status_badge.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_status_badge.tsx @@ -8,7 +8,6 @@ import React from 'react'; import { EuiBadge, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { euiLightVars } from '@kbn/ui-theme'; import { SLOWithSummaryResponse } from '@kbn/slo-schema'; export interface SloStatusProps { @@ -21,7 +20,7 @@ export function SloStatusBadge({ slo }: SloStatusProps) {
    {slo.summary.status === 'NO_DATA' && ( - + {i18n.translate('xpack.observability.slos.slo.state.noData', { defaultMessage: 'No data', })} @@ -29,7 +28,7 @@ export function SloStatusBadge({ slo }: SloStatusProps) { )} {slo.summary.status === 'HEALTHY' && ( - + {i18n.translate('xpack.observability.slos.slo.state.healthy', { defaultMessage: 'Healthy', })} @@ -37,7 +36,7 @@ export function SloStatusBadge({ slo }: SloStatusProps) { )} {slo.summary.status === 'DEGRADING' && ( - + {i18n.translate('xpack.observability.slos.slo.state.degrading', { defaultMessage: 'Degrading', })} @@ -45,7 +44,7 @@ export function SloStatusBadge({ slo }: SloStatusProps) { )} {slo.summary.status === 'VIOLATED' && ( - + {i18n.translate('xpack.observability.slos.slo.state.violated', { defaultMessage: 'Violated', })} @@ -56,7 +55,7 @@ export function SloStatusBadge({ slo }: SloStatusProps) { {slo.summary.errorBudget.isEstimated && (
    - + {i18n.translate('xpack.observability.slos.slo.state.forecasted', { defaultMessage: 'Forecasted', })} diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_list_item.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_list_item.tsx index a56d48698d540..282b82bf41b7e 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_list_item.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_list_item.tsx @@ -20,6 +20,7 @@ import { import { i18n } from '@kbn/i18n'; import { HistoricalSummaryResponse, SLOWithSummaryResponse } from '@kbn/slo-schema'; +import { ActiveAlerts } from '../../../hooks/slo/use_fetch_active_alerts'; import { useCapabilities } from '../../../hooks/slo/use_capabilities'; import { useKibana } from '../../../utils/kibana_react'; import { useCloneSlo } from '../../../hooks/slo/use_clone_slo'; @@ -36,12 +37,14 @@ export interface SloListItemProps { slo: SLOWithSummaryResponse; historicalSummary?: HistoricalSummaryResponse[]; historicalSummaryLoading: boolean; + activeAlerts?: ActiveAlerts; } export function SloListItem({ slo, historicalSummary = [], historicalSummaryLoading, + activeAlerts, }: SloListItemProps) { const { application: { navigateToUrl }, @@ -101,7 +104,7 @@ export function SloListItem({ {slo.name} - + diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_list_items.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_list_items.tsx index 2e0bb5b267b7a..85ce283c90b18 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_list_items.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_list_items.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { SLOWithSummaryResponse } from '@kbn/slo-schema'; +import { useFetchActiveAlerts } from '../../../hooks/slo/use_fetch_active_alerts'; import { useFetchHistoricalSummary } from '../../../hooks/slo/use_fetch_historical_summary'; import { SloListItem } from './slo_list_item'; import { SloListEmpty } from './slo_list_empty'; @@ -23,6 +24,10 @@ export function SloListItems({ sloList, loading, error }: Props) { const { isLoading: historicalSummaryLoading, sloHistoricalSummaryResponse } = useFetchHistoricalSummary({ sloIds: sloList.map((slo) => slo.id) }); + const { data: activeAlertsBySlo } = useFetchActiveAlerts({ + sloIds: sloList.map((slo) => slo.id), + }); + if (!loading && !error && sloList.length === 0) { return ; } @@ -38,6 +43,7 @@ export function SloListItems({ sloList, loading, error }: Props) { slo={slo} historicalSummary={sloHistoricalSummaryResponse[slo.id]} historicalSummaryLoading={historicalSummaryLoading} + activeAlerts={activeAlertsBySlo[slo.id]} /> ))} diff --git a/x-pack/plugins/observability/server/plugin.ts b/x-pack/plugins/observability/server/plugin.ts index 98b21abbaefe5..39a69d7fa1666 100644 --- a/x-pack/plugins/observability/server/plugin.ts +++ b/x-pack/plugins/observability/server/plugin.ts @@ -18,10 +18,10 @@ import { Dataset, RuleRegistryPluginSetupContract } from '@kbn/rule-registry-plu import { PluginSetupContract as FeaturesSetup } from '@kbn/features-plugin/server'; import { createUICapabilities } from '@kbn/cases-plugin/common'; import { experimentalRuleFieldMap } from '@kbn/rule-registry-plugin/common/assets/field_maps/experimental_rule_field_map'; -import { mappingFromFieldMap } from '@kbn/rule-registry-plugin/common/mapping_from_field_map'; -import { ECS_COMPONENT_TEMPLATE_NAME } from '@kbn/rule-registry-plugin/common/assets'; +import { ECS_COMPONENT_TEMPLATE_NAME } from '@kbn/alerting-plugin/server'; import type { GuidedOnboardingPluginSetup } from '@kbn/guided-onboarding-plugin/server'; +import { mappingFromFieldMap } from '@kbn/alerting-plugin/common'; import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server'; import { kubernetesGuideId, diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.ts b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.ts index 7723bd5ce3f99..165d40cf07c37 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.ts +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.ts @@ -22,6 +22,7 @@ import { SLO, APMTransactionDurationIndicator } from '../../../domain/models'; import { getElastichsearchQueryOrThrow, TransformGenerator } from '.'; import { DEFAULT_APM_INDEX } from './constants'; import { Query } from './types'; +import { parseIndex } from './common'; export class ApmTransactionDurationTransformGenerator extends TransformGenerator { public getTransformParams(slo: SLO): TransformPutTransformRequest { @@ -91,7 +92,7 @@ export class ApmTransactionDurationTransformGenerator extends TransformGenerator } return { - index: indicator.params.index ?? DEFAULT_APM_INDEX, + index: parseIndex(indicator.params.index ?? DEFAULT_APM_INDEX), runtime_mappings: this.buildCommonRuntimeMappings(slo), query: { bool: { diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_error_rate.ts b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_error_rate.ts index 3c26ebfcf146e..6288bcfe3ab4e 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_error_rate.ts +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_error_rate.ts @@ -23,6 +23,7 @@ import { import { APMTransactionErrorRateIndicator, SLO } from '../../../domain/models'; import { DEFAULT_APM_INDEX } from './constants'; import { Query } from './types'; +import { parseIndex } from './common'; const ALLOWED_STATUS_CODES = ['2xx', '3xx', '4xx', '5xx']; const DEFAULT_GOOD_STATUS_CODES = ['2xx', '3xx', '4xx']; @@ -96,7 +97,7 @@ export class ApmTransactionErrorRateTransformGenerator extends TransformGenerato } return { - index: indicator.params.index ?? DEFAULT_APM_INDEX, + index: parseIndex(indicator.params.index ?? DEFAULT_APM_INDEX), runtime_mappings: this.buildCommonRuntimeMappings(slo), query: { bool: { diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/common.test.ts b/x-pack/plugins/observability/server/services/slo/transform_generators/common.test.ts new file mode 100644 index 0000000000000..ba9856860ec84 --- /dev/null +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/common.test.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 { parseIndex } from './common'; + +describe('common', () => { + describe('parseIndex', () => { + it.each([ + ['foo-*', 'foo-*'], + ['foo-*,bar-*', ['foo-*', 'bar-*']], + ['remote:foo-*', 'remote:foo-*'], + ['remote:foo*,bar-*', ['remote:foo*', 'remote:bar-*']], + ])("parses the index '%s' correctly", (index, expected) => { + expect(parseIndex(index)).toEqual(expected); + }); + }); +}); diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/common.ts b/x-pack/plugins/observability/server/services/slo/transform_generators/common.ts index b9426ef5f5a77..54197076d359f 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/common.ts +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/common.ts @@ -15,3 +15,17 @@ export function getElastichsearchQueryOrThrow(kuery: string) { throw new InvalidTransformError(`Invalid KQL: ${kuery}`); } } + +export function parseIndex(index: string): string | string[] { + if (index.indexOf(',') > -1) { + if (index.indexOf(':') > -1) { + const indexParts = index.split(':'); // "remote_name:foo-*,bar*" + const remoteName = indexParts[0]; + return indexParts[1].split(',').map((idx) => `${remoteName}:${idx}`); // [ "remote_name:foo-*", "remote_name:bar-*"] + } + + return index.split(','); + } + + return index; +} diff --git a/x-pack/plugins/observability/server/services/slo/update_slo.ts b/x-pack/plugins/observability/server/services/slo/update_slo.ts index 9b4aa4e7dc213..cf2c0e2e1c89f 100644 --- a/x-pack/plugins/observability/server/services/slo/update_slo.ts +++ b/x-pack/plugins/observability/server/services/slo/update_slo.ts @@ -6,7 +6,6 @@ */ import deepEqual from 'fast-deep-equal'; -import merge from 'lodash/merge'; import { ElasticsearchClient } from '@kbn/core/server'; import { UpdateSLOParams, UpdateSLOResponse, updateSLOResponseSchema } from '@kbn/slo-schema'; @@ -42,7 +41,7 @@ export class UpdateSLO { private updateSLO(originalSlo: SLO, params: UpdateSLOParams) { let hasBreakingChange = false; - const updatedSlo: SLO = merge({}, originalSlo, params, { updatedAt: new Date() }); + const updatedSlo: SLO = Object.assign({}, originalSlo, params, { updatedAt: new Date() }); validateSLO(updatedSlo); if (!deepEqual(originalSlo.indicator, updatedSlo.indicator)) { diff --git a/x-pack/plugins/osquery/common/utils/replace_params_query.ts b/x-pack/plugins/osquery/common/utils/replace_params_query.ts index 3c99daf5ec3cf..d06be33a87330 100644 --- a/x-pack/plugins/osquery/common/utils/replace_params_query.ts +++ b/x-pack/plugins/osquery/common/utils/replace_params_query.ts @@ -10,6 +10,10 @@ import { each, get } from 'lodash'; const CONTAINS_DYNAMIC_PARAMETER_REGEX = /\{{([^}]+)\}}/g; // when there are 2 opening and 2 closing curly brackets (including brackets) export const replaceParamsQuery = (query: string, data: object) => { + if (!containsDynamicQuery(query)) { + return { result: query, skipped: false }; + } + const matchedBrackets = query.match(new RegExp(CONTAINS_DYNAMIC_PARAMETER_REGEX)); let resultQuery = query; diff --git a/x-pack/plugins/osquery/cypress/e2e/all/add_integration.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/add_integration.cy.ts index 35c5c2aa15503..aaf5fc319be15 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/add_integration.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/add_integration.cy.ts @@ -16,7 +16,7 @@ import { addIntegration, closeModalIfVisible, closeToastIfVisible } from '../../ import { login } from '../../tasks/login'; import { findAndClickButton, findFormFieldByRowsLabelAndType } from '../../tasks/live_query'; import { ArchiverMethod, runKbnArchiverScript } from '../../tasks/archiver'; -import { DEFAULT_POLICY } from '../../screens/fleet'; +import { DEFAULT_POLICY, OSQUERY_POLICY } from '../../screens/fleet'; describe('ALL - Add Integration', () => { const integration = 'Osquery Manager'; @@ -43,11 +43,10 @@ describe('ALL - Add Integration', () => { cy.get('[title="Osquery Manager • Integration"]').should('exist').click(); }); - it('should add the old integration and be able to upgrade it', () => { + it.skip('should add the old integration and be able to upgrade it', () => { const oldVersion = '0.7.4'; cy.visit(OLD_OSQUERY_MANAGER); - cy.contains(integration).click(); addIntegration(); cy.contains('osquery_manager-1'); cy.visit('app/fleet/policies'); @@ -94,11 +93,19 @@ describe('ALL - Add Integration', () => { addIntegration(); cy.contains('osquery_manager-'); closeToastIfVisible(); - cy.getBySel('nav-search-input').type('Osquery'); - cy.get('[title="Osquery • Management"]').should('exist').click(); + cy.visit(OSQUERY); cy.contains('Live queries history'); }); + it(`add integration to ${OSQUERY_POLICY}`, () => { + cy.visit(FLEET_AGENT_POLICIES); + cy.contains(OSQUERY_POLICY).click(); + cy.contains('Add integration').click(); + cy.contains(integration).click(); + addIntegration(OSQUERY_POLICY); + cy.contains('osquery_manager-'); + }); + it('should have integration and packs copied when upgrading integration', () => { const packageName = 'osquery_manager'; const oldVersion = '1.2.0'; @@ -110,12 +117,12 @@ describe('ALL - Add Integration', () => { cy.contains('Upgrade'); cy.contains('Agent policy 1').click(); cy.get('tr') - .should('contain', 'osquery_manager-2') + .should('contain', 'osquery_manager-3') .and('contain', 'Osquery Manager') .and('contain', `v${oldVersion}`); cy.contains('Actions').click(); cy.contains('View policy').click(); - cy.contains('name: osquery_manager-2'); + cy.contains('name: osquery_manager-3'); cy.contains(`version: ${oldVersion}`); cy.get('.euiFlyoutFooter').within(() => { cy.contains('Close').click(); @@ -142,19 +149,19 @@ describe('ALL - Add Integration', () => { cy.contains(/^Advanced$/).click(); cy.contains('"Integration":'); cy.contains(/^Upgrade integration$/).click(); - cy.contains(/^osquery_manager-2$/).click(); + cy.contains(/^osquery_manager-3$/).click(); cy.contains(/^Advanced$/).click(); cy.contains('"Integration":'); cy.contains('Cancel').click(); closeModalIfVisible(); cy.get('tr') - .should('contain', 'osquery_manager-2') + .should('contain', 'osquery_manager-3') .and('contain', 'Osquery Manager') .and('contain', 'v') .and('not.contain', `v${oldVersion}`); cy.contains('Actions').click(); cy.contains('View policy').click(); - cy.contains('name: osquery_manager-2'); + cy.contains('name: osquery_manager-3'); // test list of prebuilt queries navigateTo('/app/osquery/saved_queries'); diff --git a/x-pack/plugins/osquery/cypress/e2e/all/alerts.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/alerts.cy.ts index 416fdfe5b971a..2f313d1bb9979 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/alerts.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/alerts.cy.ts @@ -58,7 +58,7 @@ describe('Alert Event Details', () => { it('should prepare packs and alert rules', () => { const PACK_NAME = 'testpack'; - navigateTo('/app/osquery/packs'); + navigateTo('/app/osquery/live_queries'); preparePack(PACK_NAME); findAndClickButton('Edit'); cy.contains(`Edit ${PACK_NAME}`); @@ -439,6 +439,27 @@ describe('Alert Event Details', () => { }); }); + it('should be able to run take action query against all enrolled agents', () => { + loadAlertsEvents(); + cy.getBySel('expand-event').first().click({ force: true }); + cy.getBySel('take-action-dropdown-btn').click(); + cy.getBySel('osquery-action-item').click(); + cy.getBySel('agentSelection').within(() => { + cy.getBySel('comboBoxClearButton').click(); + cy.getBySel('comboBoxInput').type('All{downArrow}{enter}{esc}'); + cy.contains('All agents'); + }); + inputQuery("SELECT * FROM os_version where name='{{host.os.name}}';", { + parseSpecialCharSequences: false, + }); + cy.wait(1000); + submitQuery(); + cy.getBySel('flyout-body-osquery').within(() => { + // at least 2 agents should have responded + cy.get('[data-grid-row-index]').should('have.length.at.least', 2); + }); + }); + it('should substitute params in osquery ran from timelines alerts', () => { loadAlertsEvents(); cy.getBySel('send-alert-to-timeline-button').first().click({ force: true }); diff --git a/x-pack/plugins/osquery/cypress/e2e/all/custom_space.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/custom_space.cy.ts index e58f6c689eb55..3aaeff293ac47 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/custom_space.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/custom_space.cy.ts @@ -29,6 +29,7 @@ describe('ALL - Custom space', () => { id: CUSTOM_SPACE, name: CUSTOM_SPACE, }, + failOnStatusCode: false, headers: { 'kbn-xsrf': 'create-space' }, }); }); diff --git a/x-pack/plugins/osquery/cypress/e2e/all/live_query.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/live_query.cy.ts index 8991858bd7c26..aa21b6142a9df 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/live_query.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/live_query.cy.ts @@ -68,6 +68,7 @@ describe('ALL - Live Query', () => { checkResults(); cy.react('Cell', { props: { columnIndex: 0 } }) .should('exist') + .first() .click(); cy.url().should('include', 'app/fleet/agents/'); }); diff --git a/x-pack/plugins/osquery/cypress/e2e/all/metrics.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/metrics.cy.ts index 0edefdc24ea42..998d5220e24f4 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/metrics.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/metrics.cy.ts @@ -29,19 +29,20 @@ describe('ALL - Inventory', () => { cy.wait(1000); - cy.getBySel('nodeContainer').click(); + cy.getBySel('nodeContainer').first().click(); cy.contains('Osquery').click(); inputQuery('select * from uptime;'); submitQuery(); checkResults(); }); + it('should be able to run the previously saved query', () => { cy.getBySel('toggleNavButton').click(); cy.getBySel('collapsibleNavAppLink').contains('Infrastructure').click(); cy.wait(500); - cy.getBySel('nodeContainer').click(); + cy.getBySel('nodeContainer').first().click(); cy.contains('Osquery').click(); cy.getBySel('comboBoxInput').first().click(); diff --git a/x-pack/plugins/osquery/cypress/e2e/all/packs.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/packs.cy.ts index d402412dcdbd7..74253324acdfb 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/packs.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/packs.cy.ts @@ -476,9 +476,8 @@ describe('ALL - Packs', () => { navigateTo('/app/osquery/packs'); }); - it('add global packs to polciies', () => { + it('add global packs to policies', () => { const globalPack = 'globalPack'; - cy.contains('Packs').click(); findAndClickButton('Add pack'); findFormFieldByRowsLabelAndType('Name', globalPack); cy.getBySel('policyIdsComboBox').should('exist'); @@ -517,9 +516,9 @@ describe('ALL - Packs', () => { cy.contains('rev. 2').click(); }); }); + it('add proper shard to policies packs config', () => { const shardPack = 'shardPack'; - cy.contains('Packs').click(); cy.getBySel('pagination-button-next').click(); findAndClickButton('Add pack'); 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 9605984969c00..8af212661f741 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 @@ -64,10 +64,12 @@ describe('ALL - Saved queries', () => { cy.contains('Snapshot'); }); }); + describe('prebuilt ', () => { before(() => { runKbnArchiverScript(ArchiverMethod.LOAD, 'pack_with_prebuilt_saved_queries'); }); + beforeEach(() => { navigateTo('/app/osquery/saved_queries'); }); @@ -77,7 +79,6 @@ describe('ALL - Saved queries', () => { }); it('checks result type on prebuilt saved query', () => { - cy.contains('Saved queries').click(); cy.react('CustomItemAction', { props: { index: 1, item: { attributes: { id: 'users_elastic' } } }, }).click(); @@ -85,6 +86,7 @@ describe('ALL - Saved queries', () => { cy.contains('Snapshot'); }); }); + it('user can run prebuilt saved query and add to case', () => { cy.react('PlayButtonComponent', { props: { savedQuery: { attributes: { id: 'users_elastic' } } }, @@ -98,7 +100,6 @@ describe('ALL - Saved queries', () => { }); it('user cant delete prebuilt saved query', () => { - cy.contains('Saved queries').click(); cy.react('CustomItemAction', { props: { index: 1, item: { attributes: { id: 'users_elastic' } } }, }).click(); @@ -114,6 +115,7 @@ describe('ALL - Saved queries', () => { }).click(); deleteAndConfirm('query'); }); + it('user can edit prebuilt saved query under pack', () => { const PACK_NAME = 'pack_with_prebuilt_sq'; preparePack(PACK_NAME); diff --git a/x-pack/plugins/osquery/cypress/screens/fleet.ts b/x-pack/plugins/osquery/cypress/screens/fleet.ts index b7cce6484c405..3f5370126f95f 100644 --- a/x-pack/plugins/osquery/cypress/screens/fleet.ts +++ b/x-pack/plugins/osquery/cypress/screens/fleet.ts @@ -10,3 +10,4 @@ export const ADD_AGENT_BUTTON = 'addAgentButton'; export const AGENT_POLICIES_TAB = 'fleet-agent-policies-tab'; export const ENROLLMENT_TOKENS_TAB = 'fleet-enrollment-tokens-tab'; export const DEFAULT_POLICY = 'Default Fleet Server policy'; +export const OSQUERY_POLICY = 'Osquery policy'; diff --git a/x-pack/plugins/osquery/cypress/tasks/live_query.ts b/x-pack/plugins/osquery/cypress/tasks/live_query.ts index 5e1c474da2d78..c37dbc7766bf4 100644 --- a/x-pack/plugins/osquery/cypress/tasks/live_query.ts +++ b/x-pack/plugins/osquery/cypress/tasks/live_query.ts @@ -17,7 +17,7 @@ export const selectAllAgents = () => { }).click(); cy.react('EuiFilterSelectItem').contains('All agents').should('exist'); cy.react('AgentsTable EuiComboBox').type('{downArrow}{enter}{esc}'); - cy.contains('1 agent selected.'); + cy.contains('2 agents selected.'); }; export const clearInputQuery = () => diff --git a/x-pack/plugins/osquery/cypress/tasks/navigation.ts b/x-pack/plugins/osquery/cypress/tasks/navigation.ts index 7b1505eecd698..9cbd0fac297a2 100644 --- a/x-pack/plugins/osquery/cypress/tasks/navigation.ts +++ b/x-pack/plugins/osquery/cypress/tasks/navigation.ts @@ -6,6 +6,7 @@ */ import { TOGGLE_NAVIGATION_BTN } from '../screens/navigation'; +import { closeToastIfVisible } from './integrations'; export const INTEGRATIONS = 'app/integrations#/'; export const FLEET = 'app/fleet/'; @@ -20,7 +21,7 @@ export const navigateTo = (page: string, opts?: Partial) = cy.contains('Loading Elastic').should('not.exist'); // There's a security warning toast that seemingly makes ui elements in the bottom right unavailable, so we close it - cy.get('[data-test-subj="toastCloseButton"]', { timeout: 30000 }).click(); + closeToastIfVisible(); cy.waitForReact(); }; diff --git a/x-pack/plugins/osquery/cypress/tasks/packs.ts b/x-pack/plugins/osquery/cypress/tasks/packs.ts index 6d74ac2110a0c..80f9a497ef6b4 100644 --- a/x-pack/plugins/osquery/cypress/tasks/packs.ts +++ b/x-pack/plugins/osquery/cypress/tasks/packs.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { closeModalIfVisible } from './integrations'; +import { closeModalIfVisible, closeToastIfVisible } from './integrations'; export const preparePack = (packName: string) => { cy.contains('Packs').click(); @@ -21,7 +21,7 @@ export const deactivatePack = (packName: string) => { cy.contains(`Successfully deactivated "${packName}" pack`).should('not.exist'); cy.contains(`Successfully deactivated "${packName}" pack`).should('exist'); - cy.getBySel('toastCloseButton').click(); + closeToastIfVisible(); }; export const activatePack = (packName: string) => { @@ -32,5 +32,5 @@ export const activatePack = (packName: string) => { cy.contains(`Successfully activated "${packName}" pack`).should('not.exist'); cy.contains(`Successfully activated "${packName}" pack`).should('exist'); - cy.getBySel('toastCloseButton').click(); + closeToastIfVisible(); }; diff --git a/x-pack/plugins/osquery/cypress/tasks/saved_queries.ts b/x-pack/plugins/osquery/cypress/tasks/saved_queries.ts index 7c70d8cdb3a2b..780880b838caf 100644 --- a/x-pack/plugins/osquery/cypress/tasks/saved_queries.ts +++ b/x-pack/plugins/osquery/cypress/tasks/saved_queries.ts @@ -6,6 +6,7 @@ */ import { RESULTS_TABLE_BUTTON } from '../screens/live_query'; +import { closeToastIfVisible } from './integrations'; import { checkResults, BIG_QUERY, @@ -15,6 +16,7 @@ import { selectAllAgents, submitQuery, } from './live_query'; +import { navigateTo } from './navigation'; export const getSavedQueriesComplexTest = (savedQueryId: string, savedQueryDescription: string) => it( @@ -71,7 +73,7 @@ export const getSavedQueriesComplexTest = (savedQueryId: string, savedQueryDescr // visit Status results cy.react('EuiTab', { props: { id: 'status' } }).click(); - cy.react('EuiTableRow').should('have.lengthOf', 1); + cy.react('EuiTableRow').should('have.lengthOf', 2); // save new query cy.contains('Exit full screen').should('not.exist'); @@ -81,10 +83,10 @@ export const getSavedQueriesComplexTest = (savedQueryId: string, savedQueryDescr findFormFieldByRowsLabelAndType('Description (optional)', savedQueryDescription); cy.react('EuiButtonDisplay').contains('Save').click(); cy.contains('Successfully saved'); - cy.getBySel('toastCloseButton').click(); + closeToastIfVisible(); // play saved query - cy.contains('Saved queries').click(); + navigateTo('/app/osquery/saved_queries'); cy.contains(savedQueryId); cy.react('PlayButtonComponent', { props: { savedQuery: { attributes: { id: savedQueryId } } }, diff --git a/x-pack/plugins/osquery/server/handlers/action/create_action_handler.ts b/x-pack/plugins/osquery/server/handlers/action/create_action_handler.ts index 4974fc21b440b..b2f6ca09234eb 100644 --- a/x-pack/plugins/osquery/server/handlers/action/create_action_handler.ts +++ b/x-pack/plugins/osquery/server/handlers/action/create_action_handler.ts @@ -11,11 +11,7 @@ import { filter, flatten, isEmpty, map, omit, pick, pickBy, some } from 'lodash' import { AGENT_ACTIONS_INDEX } from '@kbn/fleet-plugin/common'; import type { SavedObjectsClientContract } from '@kbn/core/server'; import type { ParsedTechnicalFields } from '@kbn/rule-registry-plugin/common'; -import { - containsDynamicQuery, - replaceParamsQuery, -} from '../../../common/utils/replace_params_query'; -import { createDynamicQueries, createQueries } from './create_queries'; +import { createDynamicQueries, replacedQueries } from './create_queries'; import { getInternalSavedObjectsClient } from '../../routes/utils'; import { parseAgentSelection } from '../../lib/parse_agent_groups'; import { packSavedObjectType } from '../../../common/types'; @@ -94,16 +90,13 @@ export const createActionHandler = async ( : undefined, queries: packSO ? map(convertSOQueriesToPack(packSO.attributes.queries), (packQuery, packQueryId) => { - const dynamicQueryPresent = packQuery.query && containsDynamicQuery(packQuery.query); + const replacedQuery = replacedQueries(packQuery.query, alertData); return pickBy( { action_id: uuidv4(), id: packQueryId, - query: - dynamicQueryPresent && alertData - ? replaceParamsQuery(packQuery.query, alertData).result - : packQuery.query, + ...replacedQuery, ecs_mapping: packQuery.ecs_mapping, version: packQuery.version, platform: packQuery.platform, @@ -112,9 +105,7 @@ export const createActionHandler = async ( (value) => !isEmpty(value) ); }) - : alertData - ? await createDynamicQueries(params, alertData, osqueryContext) - : await createQueries(params, selectedAgents, osqueryContext), + : await createDynamicQueries({ params, alertData, agents: selectedAgents, osqueryContext }), }; const fleetActions = map( @@ -130,31 +121,33 @@ export const createActionHandler = async ( data: pick(query, ['id', 'query', 'ecs_mapping', 'version', 'platform']), }) ); + if (fleetActions.length) { + await esClientInternal.bulk({ + refresh: 'wait_for', + body: flatten( + fleetActions.map((action) => [{ index: { _index: AGENT_ACTIONS_INDEX } }, action]) + ), + }); - await esClientInternal.bulk({ - refresh: 'wait_for', - body: flatten( - fleetActions.map((action) => [{ index: { _index: AGENT_ACTIONS_INDEX } }, action]) - ), - }); + const actionsComponentTemplateExists = await esClientInternal.indices.exists({ + index: `${ACTIONS_INDEX}*`, + }); - const actionsComponentTemplateExists = await esClientInternal.indices.exists({ - index: `${ACTIONS_INDEX}*`, - }); + if (actionsComponentTemplateExists) { + await esClientInternal.bulk({ + refresh: 'wait_for', + body: [{ index: { _index: `${ACTIONS_INDEX}-default` } }, osqueryAction], + }); + } - if (actionsComponentTemplateExists) { - await esClientInternal.bulk({ - refresh: 'wait_for', - body: [{ index: { _index: `${ACTIONS_INDEX}-default` } }, osqueryAction], + osqueryContext.telemetryEventsSender.reportEvent(TELEMETRY_EBT_LIVE_QUERY_EVENT, { + ...omit(osqueryAction, ['type', 'input_type', 'user_id']), + agents: osqueryAction.agents.length, }); } - osqueryContext.telemetryEventsSender.reportEvent(TELEMETRY_EBT_LIVE_QUERY_EVENT, { - ...omit(osqueryAction, ['type', 'input_type', 'user_id']), - agents: osqueryAction.agents.length, - }); - return { response: osqueryAction, + fleetActionsCount: fleetActions.length, }; }; diff --git a/x-pack/plugins/osquery/server/handlers/action/create_queries.test.ts b/x-pack/plugins/osquery/server/handlers/action/create_queries.test.ts index 3b88e710fc52b..4956dfd1245bc 100644 --- a/x-pack/plugins/osquery/server/handlers/action/create_queries.test.ts +++ b/x-pack/plugins/osquery/server/handlers/action/create_queries.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { createDynamicQueries, createQueries } from './create_queries'; +import { createDynamicQueries, PARAMETER_NOT_FOUND } from './create_queries'; import type { ParsedTechnicalFields } from '@kbn/rule-registry-plugin/common'; import type { OsqueryAppContext } from '../../lib/osquery_app_context_services'; @@ -18,6 +18,8 @@ describe('create queries', () => { removed: false, snapshot: true, }; + const TEST_AGENT = 'test-agent'; + const mockedQueriesParams = { queries: [ { @@ -49,69 +51,74 @@ describe('create queries', () => { // Info: getting queries by index (eg. [1], [0]) because can't compare whole query object due to unique action_id generated. describe('dynamic', () => { const pid = 123; - it('if queries length it should return replaced list of queries', async () => { - const queries = await createDynamicQueries( - mockedQueriesParams, - { + it('alertData, multi queries, should replace queries and show errors', async () => { + const queries = await createDynamicQueries({ + params: mockedQueriesParams, + agents: [TEST_AGENT], + alertData: { process: { pid, }, } as unknown as ParsedTechnicalFields, - {} as OsqueryAppContext - ); + osqueryContext: {} as OsqueryAppContext, + }); expect(queries[0].query).toBe(`SELECT * FROM processes where pid=${pid};`); + expect(queries[0].error).toBe(undefined); + expect(queries[1].query).toBe('SELECT * FROM processes where pid={{process.not-existing}};'); expect(queries[1].error).toBe( "This query hasn't been called due to parameter used and its value not found in the alert." ); - expect(queries[1].query).toBe('SELECT * FROM processes where pid={{process.not-existing}};'); expect(queries[2].query).toBe('SELECT * FROM processes;'); + expect(queries[2].error).toBe(undefined); }); - it('if single query it should return one replaced query ', async () => { - const queries = await createDynamicQueries( - mockedSingleQueryParams, - { - process: { - pid, - }, + + it('alertData, single query, existing param should return changed query', async () => { + const queries = await createDynamicQueries({ + params: mockedSingleQueryParams, + agents: [TEST_AGENT], + alertData: { + process: { pid }, } as unknown as ParsedTechnicalFields, - {} as OsqueryAppContext - ); + osqueryContext: {} as OsqueryAppContext, + }); expect(queries[0].query).toBe(`SELECT * FROM processes where pid=${pid};`); + expect(queries[0].error).toBe(undefined); }); - it('if single query with not existing parameter it should return query as it is', async () => { - const queries = await createDynamicQueries( - mockedSingleQueryParams, - { + it('alertData, single query, not existing param should return error', async () => { + const queries = await createDynamicQueries({ + params: mockedSingleQueryParams, + agents: [TEST_AGENT], + alertData: { process: {}, } as unknown as ParsedTechnicalFields, - {} as OsqueryAppContext - ); + osqueryContext: {} as OsqueryAppContext, + }); expect(queries[0].query).toBe('SELECT * FROM processes where pid={{process.pid}};'); - expect(queries[0].error).toBe(undefined); + expect(queries[0].error).toBe(PARAMETER_NOT_FOUND); }); - }); - describe('normal', () => { - const TEST_AGENT = 'test-agent'; - it('if queries length it should return not replaced list of queries with agents', async () => { - const queries = await createQueries( - mockedQueriesParams, - [TEST_AGENT], - {} as OsqueryAppContext - ); + it('no alert data, multi query, return unchanged queries no error', async () => { + const queries = await createDynamicQueries({ + params: mockedQueriesParams, + agents: [TEST_AGENT], + osqueryContext: {} as OsqueryAppContext, + }); expect(queries[0].query).toBe('SELECT * FROM processes where pid={{process.pid}};'); expect(queries[0].agents).toContain(TEST_AGENT); + expect(queries[0].error).toBe(undefined); expect(queries[2].query).toBe('SELECT * FROM processes;'); expect(queries[2].agents).toContain(TEST_AGENT); + expect(queries[2].error).toBe(undefined); }); - it('if single query should return not replaced query with agents', async () => { - const queries = await createQueries( - mockedSingleQueryParams, - [TEST_AGENT], - {} as OsqueryAppContext - ); + it('no alert data, single query, return unchanged query and no error', async () => { + const queries = await createDynamicQueries({ + params: mockedSingleQueryParams, + agents: [TEST_AGENT], + osqueryContext: {} as OsqueryAppContext, + }); expect(queries[0].query).toBe('SELECT * FROM processes where pid={{process.pid}};'); expect(queries[0].agents).toContain(TEST_AGENT); + expect(queries[0].error).toBe(undefined); }); }); }); diff --git a/x-pack/plugins/osquery/server/handlers/action/create_queries.ts b/x-pack/plugins/osquery/server/handlers/action/create_queries.ts index 6551b8252e16b..f7d3601722189 100644 --- a/x-pack/plugins/osquery/server/handlers/action/create_queries.ts +++ b/x-pack/plugins/osquery/server/handlers/action/create_queries.ts @@ -15,47 +15,26 @@ import type { CreateLiveQueryRequestBodySchema } from '../../../common/schemas/r import { replaceParamsQuery } from '../../../common/utils/replace_params_query'; import { isSavedQueryPrebuilt } from '../../routes/saved_query/utils'; -export const createQueries = async ( - params: CreateLiveQueryRequestBodySchema, - agents: string[], - osqueryContext: OsqueryAppContext -) => - params.queries?.length - ? map(params.queries, (query) => - pickBy( - { - ...query, - action_id: uuidv4(), - agents, - }, - (value) => !isEmpty(value) || value === true - ) - ) - : [ - pickBy( - { - action_id: uuidv4(), - id: uuidv4(), - query: params.query, - saved_query_id: params.saved_query_id, - saved_query_prebuilt: params.saved_query_id - ? await isSavedQueryPrebuilt( - osqueryContext.service.getPackageService()?.asInternalUser, - params.saved_query_id - ) - : undefined, - ecs_mapping: params.ecs_mapping, - agents, - }, - (value) => !isEmpty(value) - ), - ]; +export const PARAMETER_NOT_FOUND = i18n.translate( + 'xpack.osquery.liveQueryActions.error.notFoundParameters', + { + defaultMessage: + "This query hasn't been called due to parameter used and its value not found in the alert.", + } +); -export const createDynamicQueries = async ( - params: CreateLiveQueryRequestBodySchema, - alertData: ParsedTechnicalFields, - osqueryContext: OsqueryAppContext -) => +interface CreateDynamicQueriesParams { + params: CreateLiveQueryRequestBodySchema; + alertData?: ParsedTechnicalFields; + agents: string[]; + osqueryContext: OsqueryAppContext; +} +export const createDynamicQueries = async ({ + params, + alertData, + agents, + osqueryContext, +}: CreateDynamicQueriesParams) => params.queries?.length ? map(params.queries, ({ query, ...restQuery }) => { const replacedQuery = replacedQueries(query, alertData); @@ -66,7 +45,7 @@ export const createDynamicQueries = async ( ...restQuery, action_id: uuidv4(), alert_ids: params.alert_ids, - agents: params.agent_ids, + agents, }, (value) => !isEmpty(value) || value === true ); @@ -77,8 +56,6 @@ export const createDynamicQueries = async ( action_id: uuidv4(), id: uuidv4(), ...replacedQueries(params.query, alertData), - // just for single queries - we need to overwrite the error property - error: undefined, saved_query_id: params.saved_query_id, saved_query_prebuilt: params.saved_query_id ? await isSavedQueryPrebuilt( @@ -88,13 +65,13 @@ export const createDynamicQueries = async ( : undefined, ecs_mapping: params.ecs_mapping, alert_ids: params.alert_ids, - agents: params.agent_ids, + agents, }, (value) => !isEmpty(value) ), ]; -const replacedQueries = ( +export const replacedQueries = ( query: string | undefined, alertData?: ParsedTechnicalFields ): { query: string | undefined; error?: string } => { @@ -105,10 +82,7 @@ const replacedQueries = ( query: result, ...(skipped ? { - error: i18n.translate('xpack.osquery.liveQueryActions.error.notFoundParameters', { - defaultMessage: - "This query hasn't been called due to parameter used and its value not found in the alert.", - }), + error: PARAMETER_NOT_FOUND, } : {}), }; diff --git a/x-pack/plugins/osquery/server/routes/live_query/create_live_query_route.ts b/x-pack/plugins/osquery/server/routes/live_query/create_live_query_route.ts index 92ebddf7642e7..9d7ad88da88b6 100644 --- a/x-pack/plugins/osquery/server/routes/live_query/create_live_query_route.ts +++ b/x-pack/plugins/osquery/server/routes/live_query/create_live_query_route.ts @@ -11,6 +11,7 @@ import markdown from 'remark-parse-no-trim'; import { some, filter } from 'lodash'; import deepEqual from 'fast-deep-equal'; import type { ECSMappingOrUndefined } from '@kbn/osquery-io-ts-types'; +import { PARAMETER_NOT_FOUND } from '../../handlers/action/create_queries'; import { replaceParamsQuery } from '../../../common/utils/replace_params_query'; import { createLiveQueryRequestBodySchema } from '../../../common/schemas/routes/live_query'; import type { CreateLiveQueryRequestBodySchema } from '../../../common/schemas/routes/live_query'; @@ -93,7 +94,7 @@ export const createLiveQueryRoute = (router: IRouter, osqueryContext: OsqueryApp try { const currentUser = await osqueryContext.security.authc.getCurrentUser(request)?.username; - const { response: osqueryAction } = await createActionHandler( + const { response: osqueryAction, fleetActionsCount } = await createActionHandler( osqueryContext, request.body, { @@ -102,6 +103,11 @@ export const createLiveQueryRoute = (router: IRouter, osqueryContext: OsqueryApp alertData, } ); + if (!fleetActionsCount) { + return response.badRequest({ + body: PARAMETER_NOT_FOUND, + }); + } return response.ok({ body: { data: osqueryAction }, diff --git a/x-pack/plugins/profiling/public/components/flame_graphs_view/index.tsx b/x-pack/plugins/profiling/public/components/flame_graphs_view/index.tsx index 7d249c533f3af..fab21b235c2ff 100644 --- a/x-pack/plugins/profiling/public/components/flame_graphs_view/index.tsx +++ b/x-pack/plugins/profiling/public/components/flame_graphs_view/index.tsx @@ -15,7 +15,7 @@ import { EuiTitle, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { pick } from 'lodash'; +import { get } from 'lodash'; import React, { useState } from 'react'; import { FlameGraphComparisonMode, FlameGraphNormalizationMode } from '../../../common/flamegraph'; import { useProfilingParams } from '../../hooks/use_profiling_params'; @@ -85,9 +85,31 @@ export function FlameGraphsView({ children }: { children: React.ReactElement }) const comparisonMode = 'comparisonMode' in query ? query.comparisonMode : FlameGraphComparisonMode.Absolute; - const normalizationMode = 'normalizationMode' in query ? query.normalizationMode : undefined; - const baseline = 'baseline' in query ? query.baseline : undefined; - const comparison = 'comparison' in query ? query.comparison : undefined; + const normalizationMode: FlameGraphNormalizationMode = get( + query, + 'normalizationMode', + FlameGraphNormalizationMode.Time + ); + + const baselineScale: number = get(query, 'baseline', 1); + const comparisonScale: number = get(query, 'comparison', 1); + + const totalSeconds = + (new Date(timeRange.end).getTime() - new Date(timeRange.start).getTime()) / 1000; + const totalComparisonSeconds = + (new Date(comparisonTimeRange.end!).getTime() - + new Date(comparisonTimeRange.start!).getTime()) / + 1000; + + const baselineTime = 1; + const comparisonTime = totalSeconds / totalComparisonSeconds; + + const normalizationOptions: FlameGraphNormalizationOptions = { + baselineScale, + baselineTime, + comparisonScale, + comparisonTime, + }; const { services: { fetchElasticFlamechart }, @@ -247,33 +269,25 @@ export function FlameGraphsView({ children }: { children: React.ReactElement }) { + onChange={(mode, options) => { profilingRouter.push(routePath, { path: routePath, - query: { - ...query, - ...pick(options, 'baseline', 'comparison'), - normalizationMode: options.mode, - }, + query: + mode === FlameGraphNormalizationMode.Scale + ? { + ...query, + baseline: options.baselineScale, + comparison: options.comparisonScale, + normalizationMode: mode, + } + : { + ...query, + normalizationMode: mode, + }, }); }} - totalSeconds={ - (new Date(timeRange.end).getTime() - new Date(timeRange.start).getTime()) / 1000 - } - comparisonTotalSeconds={ - (new Date(comparisonTimeRange.end!).getTime() - - new Date(comparisonTimeRange.start!).getTime()) / - 1000 - } - options={ - (normalizationMode === FlameGraphNormalizationMode.Time - ? { mode: FlameGraphNormalizationMode.Time } - : { - mode: FlameGraphNormalizationMode.Scale, - baseline, - comparison, - }) as FlameGraphNormalizationOptions - } + mode={normalizationMode} + options={normalizationOptions} /> @@ -308,8 +322,16 @@ export function FlameGraphsView({ children }: { children: React.ReactElement }) primaryFlamegraph={data?.primaryFlamegraph} comparisonFlamegraph={data?.comparisonFlamegraph} comparisonMode={comparisonMode} - baseline={baseline} - comparison={comparison} + baseline={ + normalizationMode === FlameGraphNormalizationMode.Time + ? baselineTime + : baselineScale + } + comparison={ + normalizationMode === FlameGraphNormalizationMode.Time + ? comparisonTime + : comparisonScale + } showInformationWindow={showInformationWindow} onInformationWindowClose={() => { setShowInformationWindow(false); diff --git a/x-pack/plugins/profiling/public/components/flame_graphs_view/normalization_menu.tsx b/x-pack/plugins/profiling/public/components/flame_graphs_view/normalization_menu.tsx index 9a6eaba2af677..06007bd09a767 100644 --- a/x-pack/plugins/profiling/public/components/flame_graphs_view/normalization_menu.tsx +++ b/x-pack/plugins/profiling/public/components/flame_graphs_view/normalization_menu.tsx @@ -27,19 +27,17 @@ import { i18n } from '@kbn/i18n'; import React, { useEffect, useState } from 'react'; import { FlameGraphNormalizationMode } from '../../../common/flamegraph'; -export type FlameGraphNormalizationOptions = - | { - mode: FlameGraphNormalizationMode.Scale; - baseline: number; - comparison: number; - } - | { mode: FlameGraphNormalizationMode.Time }; +export interface FlameGraphNormalizationOptions { + baselineScale: number; + baselineTime: number; + comparisonScale: number; + comparisonTime: number; +} interface Props { + mode: FlameGraphNormalizationMode; options: FlameGraphNormalizationOptions; - totalSeconds: number; - comparisonTotalSeconds: number; - onChange: (options: FlameGraphNormalizationOptions) => void; + onChange: (mode: FlameGraphNormalizationMode, options: FlameGraphNormalizationOptions) => void; } const SCALE_LABEL = i18n.translate('xpack.profiling.flameGraphNormalizationMenu.scale', { @@ -57,19 +55,6 @@ const NORMALIZE_BY_LABEL = i18n.translate( } ); -function getScaleFactorsBasedOnTime({ - totalSeconds, - comparisonTotalSeconds, -}: { - totalSeconds: number; - comparisonTotalSeconds: number; -}) { - return { - baseline: 1, - comparison: totalSeconds / comparisonTotalSeconds, - }; -} - export function NormalizationMenu(props: Props) { const [isPopoverOpen, setIsPopoverOpen] = useState(false); @@ -78,26 +63,18 @@ export function NormalizationMenu(props: Props) { const baselineScaleFactorInputId = useGeneratedHtmlId({ prefix: 'baselineScaleFactor' }); const comparisonScaleFactorInputId = useGeneratedHtmlId({ prefix: 'comparisonScaleFactor' }); + const [mode, setMode] = useState(props.mode); const [options, setOptions] = useState(props.options); useEffect(() => { + setMode(props.mode); setOptions(props.options); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ - props.options.mode, - // @ts-expect-error can't refine because ESLint will complain - props.options.baseline, - // @ts-expect-error can't refine because ESLint will complain - props.options.comparison, - ]); + }, [props.mode, props.options]); const { baseline, comparison } = - options.mode === FlameGraphNormalizationMode.Time - ? getScaleFactorsBasedOnTime({ - comparisonTotalSeconds: props.comparisonTotalSeconds, - totalSeconds: props.totalSeconds, - }) - : { comparison: options.comparison, baseline: options.baseline }; + mode === FlameGraphNormalizationMode.Time + ? { comparison: options.comparisonTime, baseline: options.baselineTime } + : { comparison: options.comparisonScale, baseline: options.baselineScale }; return ( - {props.options.mode === FlameGraphNormalizationMode.Scale ? SCALE_LABEL : TIME_LABEL} + {props.mode === FlameGraphNormalizationMode.Scale ? SCALE_LABEL : TIME_LABEL} } @@ -183,17 +160,12 @@ export function NormalizationMenu(props: Props) { buttonSize="compressed" isFullWidth onChange={(id, value) => { - setOptions((prevOptions) => ({ - ...prevOptions, - ...(id === FlameGraphNormalizationMode.Time - ? { mode: FlameGraphNormalizationMode.Time } - : { mode: FlameGraphNormalizationMode.Scale, baseline: 1, comparison: 1 }), - })); + setMode(id as FlameGraphNormalizationMode); }} legend={i18n.translate('xpack.profiling.flameGraphNormalizationMode.selectModeLegend', { defaultMessage: 'Select a normalization mode for the flamegraph', })} - idSelected={options.mode} + idSelected={mode} options={[ { id: FlameGraphNormalizationMode.Scale, @@ -223,9 +195,14 @@ export function NormalizationMenu(props: Props) { id={baselineScaleFactorInputId} value={baseline} onChange={(e) => { - setOptions((prevOptions) => ({ ...prevOptions, baseline: e.target.valueAsNumber })); + if (mode === FlameGraphNormalizationMode.Scale) { + setOptions((prevOptions) => ({ + ...prevOptions, + baselineScale: e.target.valueAsNumber, + })); + } }} - disabled={options.mode === FlameGraphNormalizationMode.Time} + disabled={mode === FlameGraphNormalizationMode.Time} /> @@ -246,18 +223,20 @@ export function NormalizationMenu(props: Props) { id={comparisonScaleFactorInputId} value={comparison} onChange={(e) => { - setOptions((prevOptions) => ({ - ...prevOptions, - comparison: e.target.valueAsNumber, - })); + if (mode === FlameGraphNormalizationMode.Scale) { + setOptions((prevOptions) => ({ + ...prevOptions, + comparisonScale: e.target.valueAsNumber, + })); + } }} - disabled={options.mode === FlameGraphNormalizationMode.Time} + disabled={mode === FlameGraphNormalizationMode.Time} /> { - props.onChange(options); + props.onChange(mode, options); setIsPopoverOpen(false); }} fullWidth diff --git a/x-pack/plugins/rule_registry/common/assets.ts b/x-pack/plugins/rule_registry/common/assets.ts index a1df09df18a8f..1e8919a3a07e4 100644 --- a/x-pack/plugins/rule_registry/common/assets.ts +++ b/x-pack/plugins/rule_registry/common/assets.ts @@ -5,5 +5,4 @@ * 2.0. */ -export const TECHNICAL_COMPONENT_TEMPLATE_NAME = `technical-mappings`; -export const ECS_COMPONENT_TEMPLATE_NAME = `ecs-mappings`; +export const TECHNICAL_COMPONENT_TEMPLATE_NAME = `.alerts-technical-mappings`; diff --git a/x-pack/plugins/rule_registry/common/assets/component_templates/ecs_component_template.ts b/x-pack/plugins/rule_registry/common/assets/component_templates/ecs_component_template.ts index 8e956ba0004a2..8f30e07a0d9dc 100644 --- a/x-pack/plugins/rule_registry/common/assets/component_templates/ecs_component_template.ts +++ b/x-pack/plugins/rule_registry/common/assets/component_templates/ecs_component_template.ts @@ -5,7 +5,7 @@ * 2.0. */ import { merge } from 'lodash'; -import { mappingFromFieldMap } from '../../mapping_from_field_map'; +import { mappingFromFieldMap } from '@kbn/alerting-plugin/common'; import { ClusterPutComponentTemplateBody } from '../../types'; import { ecsFieldMap } from '../field_maps/ecs_field_map'; import { technicalRuleFieldMap } from '../field_maps/technical_rule_field_map'; diff --git a/x-pack/plugins/rule_registry/common/assets/component_templates/technical_component_template.ts b/x-pack/plugins/rule_registry/common/assets/component_templates/technical_component_template.ts index e110be339d0a0..1315d7f0d1b58 100644 --- a/x-pack/plugins/rule_registry/common/assets/component_templates/technical_component_template.ts +++ b/x-pack/plugins/rule_registry/common/assets/component_templates/technical_component_template.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { mappingFromFieldMap } from '../../mapping_from_field_map'; +import { mappingFromFieldMap } from '@kbn/alerting-plugin/common'; import { ClusterPutComponentTemplateBody } from '../../types'; import { technicalRuleFieldMap } from '../field_maps/technical_rule_field_map'; diff --git a/x-pack/plugins/rule_registry/common/assets/field_maps/experimental_rule_field_map.test.ts b/x-pack/plugins/rule_registry/common/assets/field_maps/experimental_rule_field_map.test.ts index 4e2d591bf88bd..3a6dbc4f20982 100644 --- a/x-pack/plugins/rule_registry/common/assets/field_maps/experimental_rule_field_map.test.ts +++ b/x-pack/plugins/rule_registry/common/assets/field_maps/experimental_rule_field_map.test.ts @@ -13,10 +13,12 @@ it('matches snapshot', () => { expect(experimentalRuleFieldMap).toMatchInlineSnapshot(` Object { "kibana.alert.evaluation.threshold": Object { + "required": false, "scaling_factor": 100, "type": "scaled_float", }, "kibana.alert.evaluation.value": Object { + "required": false, "scaling_factor": 100, "type": "scaled_float", }, diff --git a/x-pack/plugins/rule_registry/common/assets/field_maps/experimental_rule_field_map.ts b/x-pack/plugins/rule_registry/common/assets/field_maps/experimental_rule_field_map.ts index 92f93015309c0..3859ebe6df9b6 100644 --- a/x-pack/plugins/rule_registry/common/assets/field_maps/experimental_rule_field_map.ts +++ b/x-pack/plugins/rule_registry/common/assets/field_maps/experimental_rule_field_map.ts @@ -8,8 +8,12 @@ import * as Fields from '../../technical_rule_data_field_names'; export const experimentalRuleFieldMap = { - [Fields.ALERT_EVALUATION_THRESHOLD]: { type: 'scaled_float', scaling_factor: 100 }, - [Fields.ALERT_EVALUATION_VALUE]: { type: 'scaled_float', scaling_factor: 100 }, + [Fields.ALERT_EVALUATION_THRESHOLD]: { + type: 'scaled_float', + scaling_factor: 100, + required: false, + }, + [Fields.ALERT_EVALUATION_VALUE]: { type: 'scaled_float', scaling_factor: 100, required: false }, } as const; export type ExperimentalRuleFieldMap = typeof experimentalRuleFieldMap; diff --git a/x-pack/plugins/rule_registry/common/assets/field_maps/technical_rule_field_map.test.ts b/x-pack/plugins/rule_registry/common/assets/field_maps/technical_rule_field_map.test.ts index e7d39a71a1d6d..7c2cc7c2a02af 100644 --- a/x-pack/plugins/rule_registry/common/assets/field_maps/technical_rule_field_map.test.ts +++ b/x-pack/plugins/rule_registry/common/assets/field_maps/technical_rule_field_map.test.ts @@ -43,15 +43,27 @@ it('matches snapshot', () => { "type": "keyword", }, "kibana.alert.duration.us": Object { + "array": false, + "required": false, "type": "long", }, "kibana.alert.end": Object { + "array": false, + "required": false, "type": "date", }, "kibana.alert.flapping": Object { + "array": false, + "required": false, + "type": "boolean", + }, + "kibana.alert.flapping_history": Object { + "array": true, + "required": false, "type": "boolean", }, "kibana.alert.instance.id": Object { + "array": false, "required": true, "type": "keyword", }, @@ -81,6 +93,7 @@ it('matches snapshot', () => { "type": "keyword", }, "kibana.alert.rule.consumer": Object { + "array": false, "required": true, "type": "keyword", }, @@ -135,10 +148,13 @@ it('matches snapshot', () => { "type": "keyword", }, "kibana.alert.rule.parameters": Object { + "array": false, "ignore_above": 4096, + "required": false, "type": "flattened", }, "kibana.alert.rule.producer": Object { + "array": false, "required": true, "type": "keyword", }, @@ -158,6 +174,7 @@ it('matches snapshot', () => { "type": "keyword", }, "kibana.alert.rule.rule_type_id": Object { + "array": false, "required": true, "type": "keyword", }, @@ -197,12 +214,17 @@ it('matches snapshot', () => { "type": "keyword", }, "kibana.alert.severity": Object { + "array": false, + "required": false, "type": "keyword", }, "kibana.alert.start": Object { + "array": false, + "required": false, "type": "date", }, "kibana.alert.status": Object { + "array": false, "required": true, "type": "keyword", }, @@ -237,10 +259,13 @@ it('matches snapshot', () => { "type": "keyword", }, "kibana.alert.time_range": Object { + "array": false, "format": "epoch_millis||strict_date_optional_time", + "required": false, "type": "date_range", }, "kibana.alert.uuid": Object { + "array": false, "required": true, "type": "keyword", }, diff --git a/x-pack/plugins/rule_registry/common/assets/field_maps/technical_rule_field_map.ts b/x-pack/plugins/rule_registry/common/assets/field_maps/technical_rule_field_map.ts index 32d1b45e44cad..ef476f468544b 100644 --- a/x-pack/plugins/rule_registry/common/assets/field_maps/technical_rule_field_map.ts +++ b/x-pack/plugins/rule_registry/common/assets/field_maps/technical_rule_field_map.ts @@ -5,225 +5,12 @@ * 2.0. */ +import { alertFieldMap, legacyAlertFieldMap } from '@kbn/alerts-as-data-utils'; import { pickWithPatterns } from '../../pick_with_patterns'; -import * as Fields from '../../technical_rule_data_field_names'; -import { ecsFieldMap } from './ecs_field_map'; export const technicalRuleFieldMap = { - ...pickWithPatterns( - ecsFieldMap, - Fields.TIMESTAMP, - Fields.EVENT_KIND, - Fields.EVENT_ACTION, - Fields.TAGS - ), - [Fields.ALERT_RULE_PARAMETERS]: { type: 'flattened', ignore_above: 4096 }, - [Fields.ALERT_RULE_TYPE_ID]: { type: 'keyword', required: true }, - [Fields.ALERT_RULE_CONSUMER]: { type: 'keyword', required: true }, - [Fields.ALERT_RULE_PRODUCER]: { type: 'keyword', required: true }, - [Fields.SPACE_IDS]: { type: 'keyword', array: true, required: true }, - [Fields.ALERT_UUID]: { type: 'keyword', required: true }, - [Fields.ALERT_INSTANCE_ID]: { type: 'keyword', required: true }, - [Fields.ALERT_START]: { type: 'date' }, - [Fields.ALERT_TIME_RANGE]: { - type: 'date_range', - format: 'epoch_millis||strict_date_optional_time', - }, - [Fields.ALERT_END]: { type: 'date' }, - [Fields.ALERT_DURATION]: { type: 'long' }, - [Fields.ALERT_SEVERITY]: { type: 'keyword' }, - [Fields.ALERT_STATUS]: { type: 'keyword', required: true }, - [Fields.ALERT_FLAPPING]: { type: 'boolean' }, - [Fields.VERSION]: { - type: 'version', - array: false, - required: false, - }, - [Fields.ECS_VERSION]: { - type: 'keyword', - array: false, - required: false, - }, - [Fields.ALERT_RISK_SCORE]: { - type: 'float', - array: false, - required: false, - }, - [Fields.ALERT_WORKFLOW_STATUS]: { - type: 'keyword', - array: false, - required: false, - }, - [Fields.ALERT_WORKFLOW_USER]: { - type: 'keyword', - array: false, - required: false, - }, - [Fields.ALERT_WORKFLOW_REASON]: { - type: 'keyword', - array: false, - required: false, - }, - [Fields.ALERT_SYSTEM_STATUS]: { - type: 'keyword', - array: false, - required: false, - }, - [Fields.ALERT_ACTION_GROUP]: { - type: 'keyword', - array: false, - required: false, - }, - [Fields.ALERT_REASON]: { - type: 'keyword', - array: false, - required: false, - }, - [Fields.ALERT_CASE_IDS]: { - type: 'keyword', - array: true, - required: false, - }, - [Fields.ALERT_RULE_AUTHOR]: { - type: 'keyword', - array: false, - required: false, - }, - [Fields.ALERT_RULE_CATEGORY]: { - type: 'keyword', - array: false, - required: true, - }, - [Fields.ALERT_RULE_UUID]: { - type: 'keyword', - array: false, - required: true, - }, - [Fields.ALERT_RULE_CREATED_AT]: { - type: 'date', - array: false, - required: false, - }, - [Fields.ALERT_RULE_CREATED_BY]: { - type: 'keyword', - array: false, - required: false, - }, - [Fields.ALERT_RULE_DESCRIPTION]: { - type: 'keyword', - array: false, - required: false, - }, - [Fields.ALERT_RULE_ENABLED]: { - type: 'keyword', - array: false, - required: false, - }, - [Fields.ALERT_RULE_EXECUTION_UUID]: { - type: 'keyword', - array: false, - required: false, - }, - [Fields.ALERT_RULE_FROM]: { - type: 'keyword', - array: false, - required: false, - }, - [Fields.ALERT_RULE_INTERVAL]: { - type: 'keyword', - array: false, - required: false, - }, - [Fields.ALERT_RULE_LICENSE]: { - type: 'keyword', - array: false, - required: false, - }, - [Fields.ALERT_RULE_NAME]: { - type: 'keyword', - array: false, - required: true, - }, - [Fields.ALERT_RULE_NOTE]: { - type: 'keyword', - array: false, - required: false, - }, - [Fields.ALERT_RULE_REFERENCES]: { - type: 'keyword', - array: true, - required: false, - }, - [Fields.ALERT_RULE_RULE_ID]: { - type: 'keyword', - array: false, - required: false, - }, - [Fields.ALERT_RULE_RULE_NAME_OVERRIDE]: { - type: 'keyword', - array: false, - required: false, - }, - [Fields.ALERT_RULE_TAGS]: { - type: 'keyword', - array: true, - required: false, - }, - [Fields.ALERT_RULE_TO]: { - type: 'keyword', - array: false, - required: false, - }, - [Fields.ALERT_RULE_TYPE]: { - type: 'keyword', - array: false, - required: false, - }, - [Fields.ALERT_RULE_UPDATED_AT]: { - type: 'date', - array: false, - required: false, - }, - [Fields.ALERT_RULE_UPDATED_BY]: { - type: 'keyword', - array: false, - required: false, - }, - [Fields.ALERT_RULE_VERSION]: { - type: 'keyword', - array: false, - required: false, - }, - [Fields.ALERT_SUPPRESSION_FIELD]: { - type: 'keyword', - array: true, - required: false, - }, - [Fields.ALERT_SUPPRESSION_VALUE]: { - type: 'keyword', - array: true, - required: false, - }, - [Fields.ALERT_SUPPRESSION_START]: { - type: 'date', - array: false, - required: false, - }, - [Fields.ALERT_SUPPRESSION_END]: { - type: 'date', - array: false, - required: false, - }, - [Fields.ALERT_SUPPRESSION_DOCS_COUNT]: { - type: 'long', - array: false, - required: false, - }, - [Fields.ALERT_LAST_DETECTED]: { - type: 'date', - array: false, - required: false, - }, + ...pickWithPatterns(alertFieldMap, '*'), + ...pickWithPatterns(legacyAlertFieldMap, '*'), } as const; export type TechnicalRuleFieldMap = typeof technicalRuleFieldMap; diff --git a/x-pack/plugins/rule_registry/common/field_map/index.ts b/x-pack/plugins/rule_registry/common/field_map/index.ts index fac8575b8af48..e64ba5823e673 100644 --- a/x-pack/plugins/rule_registry/common/field_map/index.ts +++ b/x-pack/plugins/rule_registry/common/field_map/index.ts @@ -7,4 +7,3 @@ export * from './merge_field_maps'; export * from './runtime_type_from_fieldmap'; -export * from './types'; diff --git a/x-pack/plugins/rule_registry/common/field_map/merge_field_maps.ts b/x-pack/plugins/rule_registry/common/field_map/merge_field_maps.ts index 124de243352ea..701bab82855d4 100644 --- a/x-pack/plugins/rule_registry/common/field_map/merge_field_maps.ts +++ b/x-pack/plugins/rule_registry/common/field_map/merge_field_maps.ts @@ -4,7 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { FieldMap } from './types'; + +import type { FieldMap } from '@kbn/alerts-as-data-utils'; export function mergeFieldMaps( first: T1, diff --git a/x-pack/plugins/rule_registry/common/field_map/runtime_type_from_fieldmap.test.ts b/x-pack/plugins/rule_registry/common/field_map/runtime_type_from_fieldmap.test.ts index 8ee71356ef706..0b724150f0dcc 100644 --- a/x-pack/plugins/rule_registry/common/field_map/runtime_type_from_fieldmap.test.ts +++ b/x-pack/plugins/rule_registry/common/field_map/runtime_type_from_fieldmap.test.ts @@ -8,11 +8,11 @@ import { runtimeTypeFromFieldMap } from './runtime_type_from_fieldmap'; describe('runtimeTypeFromFieldMap', () => { const fieldmapRt = runtimeTypeFromFieldMap({ - keywordField: { type: 'keyword' }, - longField: { type: 'long' }, - booleanField: { type: 'boolean' }, + keywordField: { type: 'keyword', required: false }, + longField: { type: 'long', required: false }, + booleanField: { type: 'boolean', required: false }, requiredKeywordField: { type: 'keyword', required: true }, - multiKeywordField: { type: 'keyword', array: true }, + multiKeywordField: { type: 'keyword', array: true, required: false }, } as const); it('accepts both singular and array fields', () => { diff --git a/x-pack/plugins/rule_registry/common/field_map/runtime_type_from_fieldmap.ts b/x-pack/plugins/rule_registry/common/field_map/runtime_type_from_fieldmap.ts index feb59f88abc7b..93e182e53af63 100644 --- a/x-pack/plugins/rule_registry/common/field_map/runtime_type_from_fieldmap.ts +++ b/x-pack/plugins/rule_registry/common/field_map/runtime_type_from_fieldmap.ts @@ -8,7 +8,7 @@ import { Optional } from 'utility-types'; import { mapValues, pickBy } from 'lodash'; import { either } from 'fp-ts/lib/Either'; import * as t from 'io-ts'; -import { FieldMap } from './types'; +import type { FieldMap } from '@kbn/alerts-as-data-utils'; const NumberFromString = new t.Type( 'NumberFromString', diff --git a/x-pack/plugins/rule_registry/common/mapping_from_field_map.ts b/x-pack/plugins/rule_registry/common/mapping_from_field_map.ts deleted file mode 100644 index 1b66496bee19b..0000000000000 --- a/x-pack/plugins/rule_registry/common/mapping_from_field_map.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { set } from '@kbn/safer-lodash-set'; -import { FieldMap } from './field_map/types'; - -export function mappingFromFieldMap( - fieldMap: FieldMap, - dynamic: 'strict' | boolean -): estypes.MappingTypeMapping { - const mappings = { - dynamic, - properties: {}, - }; - - const fields = Object.keys(fieldMap).map((key) => { - const field = fieldMap[key]; - return { - name: key, - ...field, - }; - }); - - fields.forEach((field) => { - const { name, required, array, ...rest } = field; - - set(mappings.properties, field.name.split('.').join('.properties.'), rest); - }); - - return mappings; -} diff --git a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/resource_installer.test.ts b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/resource_installer.test.ts index b63fb2aae83d0..083c4d08d4253 100644 --- a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/resource_installer.test.ts +++ b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/resource_installer.test.ts @@ -12,11 +12,9 @@ import { AlertConsumers } from '@kbn/rule-data-utils'; import { Dataset } from './index_options'; import { IndexInfo } from './index_info'; +import { ECS_COMPONENT_TEMPLATE_NAME } from '@kbn/alerting-plugin/server'; import { elasticsearchServiceMock, ElasticsearchClientMock } from '@kbn/core/server/mocks'; -import { - ECS_COMPONENT_TEMPLATE_NAME, - TECHNICAL_COMPONENT_TEMPLATE_NAME, -} from '../../common/assets'; +import { TECHNICAL_COMPONENT_TEMPLATE_NAME } from '../../common/assets'; describe('resourceInstaller', () => { let pluginStop$: Subject; @@ -82,15 +80,11 @@ describe('resourceInstaller', () => { it('should install common resources', async () => { const mockClusterClient = elasticsearchServiceMock.createElasticsearchClient(); const getClusterClient = jest.fn(() => Promise.resolve(mockClusterClient)); - const getResourceNameMock = jest - .fn() - .mockReturnValueOnce(TECHNICAL_COMPONENT_TEMPLATE_NAME) - .mockReturnValueOnce(ECS_COMPONENT_TEMPLATE_NAME); const installer = new ResourceInstaller({ logger: loggerMock.create(), isWriteEnabled: true, disabledRegistrationContexts: [], - getResourceName: getResourceNameMock, + getResourceName: jest.fn(), getClusterClient, areFrameworkAlertsEnabled: false, pluginStop$, @@ -102,26 +96,22 @@ describe('resourceInstaller', () => { expect(mockClusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(2); expect(mockClusterClient.cluster.putComponentTemplate).toHaveBeenNthCalledWith( 1, - expect.objectContaining({ name: TECHNICAL_COMPONENT_TEMPLATE_NAME }) + expect.objectContaining({ name: ECS_COMPONENT_TEMPLATE_NAME }) ); expect(mockClusterClient.cluster.putComponentTemplate).toHaveBeenNthCalledWith( 2, - expect.objectContaining({ name: ECS_COMPONENT_TEMPLATE_NAME }) + expect.objectContaining({ name: TECHNICAL_COMPONENT_TEMPLATE_NAME }) ); }); - it('should install common resources when framework alerts are enabled', async () => { + it('should install subset of common resources when framework alerts are enabled', async () => { const mockClusterClient = elasticsearchServiceMock.createElasticsearchClient(); const getClusterClient = jest.fn(() => Promise.resolve(mockClusterClient)); - const getResourceNameMock = jest - .fn() - .mockReturnValueOnce(TECHNICAL_COMPONENT_TEMPLATE_NAME) - .mockReturnValueOnce(ECS_COMPONENT_TEMPLATE_NAME); const installer = new ResourceInstaller({ logger: loggerMock.create(), isWriteEnabled: true, disabledRegistrationContexts: [], - getResourceName: getResourceNameMock, + getResourceName: jest.fn(), getClusterClient, areFrameworkAlertsEnabled: true, pluginStop$, @@ -131,15 +121,12 @@ describe('resourceInstaller', () => { // ILM policy should be handled by framework expect(mockClusterClient.ilm.putLifecycle).not.toHaveBeenCalled(); - expect(mockClusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(2); + // ECS component template should be handled by framework + expect(mockClusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(1); expect(mockClusterClient.cluster.putComponentTemplate).toHaveBeenNthCalledWith( 1, expect.objectContaining({ name: TECHNICAL_COMPONENT_TEMPLATE_NAME }) ); - expect(mockClusterClient.cluster.putComponentTemplate).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ name: ECS_COMPONENT_TEMPLATE_NAME }) - ); }); it('should install index level resources', async () => { const mockClusterClient = elasticsearchServiceMock.createElasticsearchClient(); diff --git a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/resource_installer.ts b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/resource_installer.ts index 6af288e57a4a0..59c74b81712d8 100644 --- a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/resource_installer.ts +++ b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/resource_installer.ts @@ -15,18 +15,16 @@ import type { PublicMethodsOf } from '@kbn/utility-types'; import { DEFAULT_ALERTS_ILM_POLICY, DEFAULT_ALERTS_ILM_POLICY_NAME, -} from '@kbn/alerting-plugin/server'; -import { ECS_COMPONENT_TEMPLATE_NAME, - TECHNICAL_COMPONENT_TEMPLATE_NAME, -} from '../../common/assets'; +} from '@kbn/alerting-plugin/server'; +import { TECHNICAL_COMPONENT_TEMPLATE_NAME } from '../../common/assets'; import { technicalComponentTemplate } from '../../common/assets/component_templates/technical_component_template'; import { ecsComponentTemplate } from '../../common/assets/component_templates/ecs_component_template'; import type { IndexInfo } from './index_info'; const INSTALLATION_TIMEOUT = 20 * 60 * 1000; // 20 minutes -const TOTAL_FIELDS_LIMIT = 1900; +const TOTAL_FIELDS_LIMIT = 2500; interface ConstructorOptions { getResourceName(relativeName: string): string; getClusterClient: () => Promise; @@ -98,7 +96,7 @@ export class ResourceInstaller { */ public async installCommonResources(): Promise { await this.installWithTimeout('common resources shared between all indices', async () => { - const { getResourceName, logger, areFrameworkAlertsEnabled } = this.options; + const { logger, areFrameworkAlertsEnabled } = this.options; try { // We can install them in parallel @@ -112,16 +110,15 @@ export class ResourceInstaller { name: DEFAULT_ALERTS_ILM_POLICY_NAME, body: DEFAULT_ALERTS_ILM_POLICY, }), + this.createOrUpdateComponentTemplate({ + name: ECS_COMPONENT_TEMPLATE_NAME, + body: ecsComponentTemplate, + }), ]), this.createOrUpdateComponentTemplate({ - name: getResourceName(TECHNICAL_COMPONENT_TEMPLATE_NAME), + name: TECHNICAL_COMPONENT_TEMPLATE_NAME, body: technicalComponentTemplate, }), - - this.createOrUpdateComponentTemplate({ - name: getResourceName(ECS_COMPONENT_TEMPLATE_NAME), - body: ecsComponentTemplate, - }), ]); } catch (err) { logger.error( @@ -315,7 +312,7 @@ export class ResourceInstaller { } private async installNamespacedIndexTemplate(indexInfo: IndexInfo, namespace: string) { - const { logger, getResourceName } = this.options; + const { logger } = this.options; const { componentTemplateRefs, componentTemplates, @@ -329,8 +326,7 @@ export class ResourceInstaller { logger.debug(`Installing index template for ${primaryNamespacedAlias}`); - const technicalComponentNames = [getResourceName(TECHNICAL_COMPONENT_TEMPLATE_NAME)]; - const referencedComponentNames = componentTemplateRefs.map((ref) => getResourceName(ref)); + const technicalComponentNames = [TECHNICAL_COMPONENT_TEMPLATE_NAME]; const ownComponentNames = componentTemplates.map((template) => indexInfo.getComponentTemplateName(template.name) ); @@ -365,11 +361,7 @@ export class ResourceInstaller { // - then we include own component templates registered with this index // - finally, we include technical component templates to make sure the index gets all the // mappings and settings required by all Kibana plugins using rule registry to work properly - composed_of: [ - ...referencedComponentNames, - ...ownComponentNames, - ...technicalComponentNames, - ], + composed_of: [...componentTemplateRefs, ...ownComponentNames, ...technicalComponentNames], template: { settings: { diff --git a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts index f0eef646a1f28..fdb73c85cecb1 100644 --- a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts +++ b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts @@ -314,7 +314,9 @@ export const createLifecycleExecutor = [ALERT_WORKFLOW_STATUS]: alertData?.fields[ALERT_WORKFLOW_STATUS] ?? 'open', [EVENT_KIND]: 'signal', [EVENT_ACTION]: isNew ? 'open' : isActive ? 'active' : 'close', - [TAGS]: options.rule.tags, + [TAGS]: Array.from( + new Set([...(currentAlertData?.tags ?? []), ...(options.rule.tags ?? [])]) + ), [VERSION]: ruleDataClient.kibanaVersion, [ALERT_FLAPPING]: flapping, ...(isRecovered ? { [ALERT_END]: commonRuleFields[TIMESTAMP] } : {}), diff --git a/x-pack/plugins/rule_registry/tsconfig.json b/x-pack/plugins/rule_registry/tsconfig.json index a3a2a6d373b2b..1bb9b96e6aa92 100644 --- a/x-pack/plugins/rule_registry/tsconfig.json +++ b/x-pack/plugins/rule_registry/tsconfig.json @@ -16,7 +16,6 @@ "@kbn/data-plugin", "@kbn/alerting-plugin", "@kbn/security-plugin", - "@kbn/safer-lodash-set", "@kbn/rule-data-utils", "@kbn/es-query", "@kbn/data-views-plugin", @@ -32,6 +31,7 @@ "@kbn/logging", "@kbn/securitysolution-io-ts-utils", "@kbn/share-plugin", + "@kbn/alerts-as-data-utils", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/security/server/__snapshots__/prompt_page.test.tsx.snap b/x-pack/plugins/security/server/__snapshots__/prompt_page.test.tsx.snap index 79709d6cc5573..49d0d83a5e8c6 100644 --- a/x-pack/plugins/security/server/__snapshots__/prompt_page.test.tsx.snap +++ b/x-pack/plugins/security/server/__snapshots__/prompt_page.test.tsx.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`PromptPage renders as expected with additional scripts 1`] = `"ElasticMockedFonts

    Some Title

    Some Body
    Action#1
    Action#2
    "`; +exports[`PromptPage renders as expected with additional scripts 1`] = `"ElasticMockedFonts

    Some Title

    Some Body
    Action#1
    Action#2
    "`; -exports[`PromptPage renders as expected without additional scripts 1`] = `"ElasticMockedFonts

    Some Title

    Some Body
    Action#1
    Action#2
    "`; +exports[`PromptPage renders as expected without additional scripts 1`] = `"ElasticMockedFonts

    Some Title

    Some Body
    Action#1
    Action#2
    "`; diff --git a/x-pack/plugins/security/server/authentication/__snapshots__/unauthenticated_page.test.tsx.snap b/x-pack/plugins/security/server/authentication/__snapshots__/unauthenticated_page.test.tsx.snap index cd7cf4d96e698..3f01f0ca7c8bf 100644 --- a/x-pack/plugins/security/server/authentication/__snapshots__/unauthenticated_page.test.tsx.snap +++ b/x-pack/plugins/security/server/authentication/__snapshots__/unauthenticated_page.test.tsx.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`UnauthenticatedPage renders as expected 1`] = `"ElasticMockedFonts

    We hit an authentication error

    Try logging in again, and if the problem persists, contact your system administrator.

    "`; +exports[`UnauthenticatedPage renders as expected 1`] = `"ElasticMockedFonts

    We hit an authentication error

    Try logging in again, and if the problem persists, contact your system administrator.

    "`; -exports[`UnauthenticatedPage renders as expected with custom title 1`] = `"My Company NameMockedFonts

    We hit an authentication error

    Try logging in again, and if the problem persists, contact your system administrator.

    "`; +exports[`UnauthenticatedPage renders as expected with custom title 1`] = `"My Company NameMockedFonts

    We hit an authentication error

    Try logging in again, and if the problem persists, contact your system administrator.

    "`; diff --git a/x-pack/plugins/security/server/authorization/__snapshots__/reset_session_page.test.tsx.snap b/x-pack/plugins/security/server/authorization/__snapshots__/reset_session_page.test.tsx.snap index 060229539b9ad..b8ec6b35fe6b2 100644 --- a/x-pack/plugins/security/server/authorization/__snapshots__/reset_session_page.test.tsx.snap +++ b/x-pack/plugins/security/server/authorization/__snapshots__/reset_session_page.test.tsx.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`ResetSessionPage renders as expected 1`] = `"ElasticMockedFonts

    You do not have permission to access the requested page

    Either go back to the previous page or log in as a different user.

    "`; +exports[`ResetSessionPage renders as expected 1`] = `"ElasticMockedFonts

    You do not have permission to access the requested page

    Either go back to the previous page or log in as a different user.

    "`; -exports[`ResetSessionPage renders as expected with custom page title 1`] = `"My Company NameMockedFonts

    You do not have permission to access the requested page

    Either go back to the previous page or log in as a different user.

    "`; +exports[`ResetSessionPage renders as expected with custom page title 1`] = `"My Company NameMockedFonts

    You do not have permission to access the requested page

    Either go back to the previous page or log in as a different user.

    "`; diff --git a/x-pack/plugins/security_solution/common/endpoint/types/actions.ts b/x-pack/plugins/security_solution/common/endpoint/types/actions.ts index 0a1f482c8583f..b47b4004f9a5b 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/actions.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/actions.ts @@ -203,6 +203,11 @@ export interface EndpointAction extends ActionRequestFields { // wait to send back an action result before it will timeout timeout?: number; data: EndpointActionData; + // signature of the endpoint action + signed?: { + data: string; + signature: string; + }; } export interface EndpointActionResponse { diff --git a/x-pack/plugins/security_solution/common/experimental_features.ts b/x-pack/plugins/security_solution/common/experimental_features.ts index 5863d525ca0f8..1b6d0bb022f9b 100644 --- a/x-pack/plugins/security_solution/common/experimental_features.ts +++ b/x-pack/plugins/security_solution/common/experimental_features.ts @@ -90,6 +90,10 @@ export const allowedExperimentalValues = Object.freeze({ * Enables top charts on Alerts Page */ alertsPageChartsEnabled: true, + /** + * Enables the new security flyout over the current alert details flyout + */ + securityFlyoutEnabled: false, /** * Keep DEPRECATED experimental flags that are documented to prevent failed upgrades. diff --git a/x-pack/plugins/security_solution/cypress/e2e/cases/connectors.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/cases/connectors.cy.ts index 888b492d28b1d..e85885bb1ab0d 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/cases/connectors.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/cases/connectors.cy.ts @@ -14,7 +14,7 @@ import { cleanKibana, deleteCases } from '../../tasks/common'; import { addServiceNowConnector, openAddNewConnectorOption, - selectLastConnectorCreated, + verifyNewConnectorSelected, } from '../../tasks/configure_cases'; import { login, visitWithoutDateRange } from '../../tasks/login'; @@ -58,7 +58,7 @@ describe('Cases connectors', () => { }); cy.intercept('POST', '/api/actions/connector').as('createConnector'); - cy.intercept('POST', '/api/cases/configure', (req) => { + cy.intercept('PATCH', '/api/cases/configure/*', (req) => { const connector = req.body.connector; req.reply((res) => { res.send(200, { ...configureResult, connector }); @@ -94,14 +94,15 @@ describe('Cases connectors', () => { cy.wait('@createConnector').then(({ response }) => { cy.wrap(response?.statusCode).should('eql', 200); + + verifyNewConnectorSelected(snConnector); + cy.get(TOASTER).should('have.text', "Created 'New connector'"); + cy.get(TOASTER).should('have.text', 'Saved external connection settings'); cy.get(TOASTER).should('not.exist'); - selectLastConnectorCreated(response?.body.id); - - cy.wait('@saveConnector', { timeout: 10000 }).its('response.statusCode').should('eql', 200); + cy.wait('@saveConnector').its('response.statusCode').should('eql', 200); cy.get(SERVICE_NOW_MAPPING).first().should('have.text', 'short_description'); - cy.get(TOASTER).should('have.text', 'Saved external connection settings'); }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/alert_flyout.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/alert_flyout.cy.ts deleted file mode 100644 index d2b5f88994658..0000000000000 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/alert_flyout.cy.ts +++ /dev/null @@ -1,80 +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 { - ALERT_FLYOUT, - SUMMARY_VIEW_PREVALENCE_CELL, - SUMMARY_VIEW_INVESTIGATE_IN_TIMELINE_BUTTON, - INSIGHTS_RELATED_ALERTS_BY_SESSION, - INSIGHTS_INVESTIGATE_IN_TIMELINE_BUTTON, - INSIGHTS_RELATED_ALERTS_BY_ANCESTRY, - INSIGHTS_INVESTIGATE_ANCESTRY_ALERTS_IN_TIMELINE_BUTTON, -} from '../../screens/alerts_details'; -import { QUERY_TAB_BUTTON, TIMELINE_TITLE } from '../../screens/timeline'; - -import { expandFirstAlert } from '../../tasks/alerts'; -import { verifyInsightCount } from '../../tasks/alerts_details'; -import { setStartDate } from '../../tasks/date_picker'; -import { closeTimeline } from '../../tasks/timeline'; -import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; -import { cleanKibana } from '../../tasks/common'; -import { waitForAlertsToPopulate } from '../../tasks/create_new_rule'; -import { login, visitWithoutDateRange } from '../../tasks/login'; - -import { getNewRule } from '../../objects/rule'; - -import { ALERTS_URL } from '../../urls/navigation'; - -describe('Alert Flyout', () => { - before(() => { - cleanKibana(); - login(); - createCustomRuleEnabled(getNewRule(), 'rule1'); - }); - - beforeEach(() => { - visitWithoutDateRange(ALERTS_URL); - const dateContainingAllEvents = 'Jul 27, 2015 @ 00:00:00.000'; - setStartDate(dateContainingAllEvents); - waitForAlertsToPopulate(); - expandFirstAlert(); - }); - - afterEach(() => { - closeTimeline(); - }); - - it('Opens a new timeline investigation (from a prevalence field)', () => { - cy.get(SUMMARY_VIEW_PREVALENCE_CELL) - .first() - .invoke('text') - .then((alertCount) => { - // Click on the first button that lets us investigate in timeline - cy.get(ALERT_FLYOUT).find(SUMMARY_VIEW_INVESTIGATE_IN_TIMELINE_BUTTON).first().click(); - - // Make sure a new timeline is created and opened - cy.get(TIMELINE_TITLE).should('contain.text', 'Untitled timeline'); - - // The alert count in this timeline should match the count shown on the alert flyout - cy.get(QUERY_TAB_BUTTON).should('contain.text', alertCount); - }); - }); - - it('Opens a new timeline investigation (from an insights module)', () => { - verifyInsightCount({ - tableSelector: INSIGHTS_RELATED_ALERTS_BY_SESSION, - investigateSelector: INSIGHTS_INVESTIGATE_IN_TIMELINE_BUTTON, - }); - }); - - it('Opens a new timeline investigation with alert ids from the process ancestry', () => { - verifyInsightCount({ - tableSelector: INSIGHTS_RELATED_ALERTS_BY_ANCESTRY, - investigateSelector: INSIGHTS_INVESTIGATE_ANCESTRY_ALERTS_IN_TIMELINE_BUTTON, - }); - }); -}); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/alerts_cell_actions.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/alerts_cell_actions.cy.ts index e5663f6f4c10b..d8087294d4ed4 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/alerts_cell_actions.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/alerts_cell_actions.cy.ts @@ -8,8 +8,10 @@ import { getNewRule } from '../../objects/rule'; import { CELL_COPY_BUTTON, FILTER_BADGE, SHOW_TOP_N_HEADER } from '../../screens/alerts'; import { + ALERT_TABLE_ACTIONS_HEADER, ALERT_TABLE_FILE_NAME_HEADER, ALERT_TABLE_FILE_NAME_VALUES, + ALERT_TABLE_SEVERITY_HEADER, ALERT_TABLE_SEVERITY_VALUES, PROVIDER_BADGE, } from '../../screens/timeline'; @@ -21,134 +23,157 @@ import { showTopNAlertProperty, clickExpandActions, filterOutAlertProperty, + closeTopNAlertProperty, } from '../../tasks/alerts'; import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; import { cleanKibana } from '../../tasks/common'; import { waitForAlertsToPopulate } from '../../tasks/create_new_rule'; import { login, visit } from '../../tasks/login'; -import { fillAddFilterForm, fillKqlQueryBar, openAddFilterPopover } from '../../tasks/search_bar'; -import { openActiveTimeline } from '../../tasks/timeline'; +import { + clearKqlQueryBar, + fillAddFilterForm, + fillKqlQueryBar, + openAddFilterPopover, + removeKqlFilter, +} from '../../tasks/search_bar'; +import { closeTimeline, openActiveTimeline, removeDataProvider } from '../../tasks/timeline'; import { ALERTS_URL } from '../../urls/navigation'; -describe('Alerts cell actions', () => { +describe('Alerts cell actions', { testIsolation: false }, () => { before(() => { cleanKibana(); login(); + createCustomRuleEnabled(getNewRule()); + visit(ALERTS_URL); + waitForAlertsToPopulate(); }); - context('Opening alerts', () => { - before(() => { - createCustomRuleEnabled(getNewRule()); + describe('Filter', () => { + afterEach(() => { + removeKqlFilter(); + scrollAlertTableColumnIntoView(ALERT_TABLE_ACTIONS_HEADER); }); - beforeEach(() => { - visit(ALERTS_URL); - waitForAlertsToPopulate(); + it('should filter for a non-empty property', () => { + cy.get(ALERT_TABLE_SEVERITY_VALUES) + .first() + .invoke('text') + .then((severityVal) => { + scrollAlertTableColumnIntoView(ALERT_TABLE_SEVERITY_HEADER); + filterForAlertProperty(ALERT_TABLE_SEVERITY_VALUES, 0); + cy.get(FILTER_BADGE).first().should('have.text', `kibana.alert.severity: ${severityVal}`); + }); }); - describe('Filter', () => { - it('should filter for a non-empty property', () => { - cy.get(ALERT_TABLE_SEVERITY_VALUES) - .first() - .invoke('text') - .then((severityVal) => { - scrollAlertTableColumnIntoView(ALERT_TABLE_SEVERITY_VALUES); - filterForAlertProperty(ALERT_TABLE_SEVERITY_VALUES, 0); - cy.get(FILTER_BADGE) - .first() - .should('have.text', `kibana.alert.severity: ${severityVal}`); - }); - }); - - it('should filter for an empty property', () => { - // add query condition to make sure the field is empty - fillKqlQueryBar('not file.name: *{enter}'); - scrollAlertTableColumnIntoView(ALERT_TABLE_FILE_NAME_HEADER); - filterForAlertProperty(ALERT_TABLE_FILE_NAME_VALUES, 0); - - cy.get(FILTER_BADGE).first().should('have.text', 'NOT file.name: exists'); - }); - - it('should filter out a non-empty property', () => { - cy.get(ALERT_TABLE_SEVERITY_VALUES) - .first() - .invoke('text') - .then((severityVal) => { - scrollAlertTableColumnIntoView(ALERT_TABLE_FILE_NAME_HEADER); - filterOutAlertProperty(ALERT_TABLE_SEVERITY_VALUES, 0); - cy.get(FILTER_BADGE) - .first() - .should('have.text', `NOT kibana.alert.severity: ${severityVal}`); - }); - }); + it('should filter for an empty property', () => { + // add query condition to make sure the field is empty + fillKqlQueryBar('not file.name: *{enter}'); - it('should filter out an empty property', () => { - // add query condition to make sure the field is empty - fillKqlQueryBar('not file.name: *{enter}'); + scrollAlertTableColumnIntoView(ALERT_TABLE_FILE_NAME_HEADER); + filterForAlertProperty(ALERT_TABLE_FILE_NAME_VALUES, 0); - scrollAlertTableColumnIntoView(ALERT_TABLE_FILE_NAME_HEADER); - filterOutAlertProperty(ALERT_TABLE_FILE_NAME_VALUES, 0); + cy.get(FILTER_BADGE).first().should('have.text', 'NOT file.name: exists'); - cy.get(FILTER_BADGE).first().should('have.text', 'file.name: exists'); - }); + clearKqlQueryBar(); }); - describe('Add to timeline', () => { - it('should add a non-empty property to default timeline', () => { - cy.get(ALERT_TABLE_SEVERITY_VALUES) - .first() - .invoke('text') - .then((severityVal) => { - scrollAlertTableColumnIntoView(ALERT_TABLE_SEVERITY_VALUES); - addAlertPropertyToTimeline(ALERT_TABLE_SEVERITY_VALUES, 0); - openActiveTimeline(); - cy.get(PROVIDER_BADGE) - .first() - .should('have.text', `kibana.alert.severity: "${severityVal}"`); - }); - }); - - it('should add an empty property to default timeline', () => { - // add condition to make sure the field is empty - openAddFilterPopover(); - fillAddFilterForm({ key: 'file.name', operator: 'does not exist' }); - scrollAlertTableColumnIntoView(ALERT_TABLE_FILE_NAME_HEADER); - addAlertPropertyToTimeline(ALERT_TABLE_FILE_NAME_VALUES, 0); - openActiveTimeline(); - cy.get(PROVIDER_BADGE).first().should('have.text', 'NOT file.name exists'); - }); + it('should filter out a non-empty property', () => { + cy.get(ALERT_TABLE_SEVERITY_VALUES) + .first() + .invoke('text') + .then((severityVal) => { + scrollAlertTableColumnIntoView(ALERT_TABLE_SEVERITY_HEADER); + filterOutAlertProperty(ALERT_TABLE_SEVERITY_VALUES, 0); + cy.get(FILTER_BADGE) + .first() + .should('have.text', `NOT kibana.alert.severity: ${severityVal}`); + }); }); - describe('Show Top N', () => { - it('should show top for a property', () => { - cy.get(ALERT_TABLE_SEVERITY_VALUES) - .first() - .invoke('text') - .then(() => { - scrollAlertTableColumnIntoView(ALERT_TABLE_SEVERITY_VALUES); - showTopNAlertProperty(ALERT_TABLE_SEVERITY_VALUES, 0); - cy.get(SHOW_TOP_N_HEADER).first().should('have.text', `Top kibana.alert.severity`); - }); - }); + it('should filter out an empty property', () => { + // add query condition to make sure the field is empty + fillKqlQueryBar('not file.name: *{enter}'); + + scrollAlertTableColumnIntoView(ALERT_TABLE_FILE_NAME_HEADER); + filterOutAlertProperty(ALERT_TABLE_FILE_NAME_VALUES, 0); + + cy.get(FILTER_BADGE).first().should('have.text', 'file.name: exists'); + + clearKqlQueryBar(); + }); + }); + + describe('Add to timeline', () => { + afterEach(() => { + removeDataProvider(); + closeTimeline(); + scrollAlertTableColumnIntoView(ALERT_TABLE_ACTIONS_HEADER); + }); + + it('should add a non-empty property to default timeline', () => { + cy.get(ALERT_TABLE_SEVERITY_VALUES) + .first() + .invoke('text') + .then((severityVal) => { + scrollAlertTableColumnIntoView(ALERT_TABLE_SEVERITY_VALUES); + addAlertPropertyToTimeline(ALERT_TABLE_SEVERITY_VALUES, 0); + openActiveTimeline(); + cy.get(PROVIDER_BADGE) + .first() + .should('have.text', `kibana.alert.severity: "${severityVal}"`); + }); + }); + + it('should add an empty property to default timeline', () => { + // add condition to make sure the field is empty + openAddFilterPopover(); + + fillAddFilterForm({ key: 'file.name', operator: 'does not exist' }); + scrollAlertTableColumnIntoView(ALERT_TABLE_FILE_NAME_HEADER); + addAlertPropertyToTimeline(ALERT_TABLE_FILE_NAME_VALUES, 0); + openActiveTimeline(); + cy.get(PROVIDER_BADGE).first().should('have.text', 'NOT file.name exists'); + }); + }); + + describe('Show Top N', () => { + afterEach(() => { + closeTopNAlertProperty(); + scrollAlertTableColumnIntoView(ALERT_TABLE_ACTIONS_HEADER); + }); + + it('should show top for a property', () => { + cy.get(ALERT_TABLE_SEVERITY_VALUES) + .first() + .invoke('text') + .then(() => { + scrollAlertTableColumnIntoView(ALERT_TABLE_SEVERITY_VALUES); + showTopNAlertProperty(ALERT_TABLE_SEVERITY_VALUES, 0); + cy.get(SHOW_TOP_N_HEADER).first().should('have.text', `Top kibana.alert.severity`); + }); + }); + }); + + describe('Copy to clipboard', () => { + afterEach(() => { + scrollAlertTableColumnIntoView(ALERT_TABLE_ACTIONS_HEADER); }); - describe('Copy to clipboard', () => { - it('should copy to clipboard', () => { - cy.get(ALERT_TABLE_SEVERITY_VALUES) - .first() - .invoke('text') - .then(() => { - scrollAlertTableColumnIntoView(ALERT_TABLE_SEVERITY_VALUES); - cy.window().then((win) => { - cy.stub(win, 'prompt').returns('DISABLED WINDOW PROMPT'); - }); - clickExpandActions(ALERT_TABLE_SEVERITY_VALUES, 0); - cy.get(CELL_COPY_BUTTON).should('exist'); - // We are not able to test the "copy to clipboard" action execution - // due to browsers security limitation accessing the clipboard services. - // We assume external `copy` library works + it('should copy to clipboard', () => { + cy.get(ALERT_TABLE_SEVERITY_VALUES) + .first() + .invoke('text') + .then(() => { + scrollAlertTableColumnIntoView(ALERT_TABLE_SEVERITY_VALUES); + cy.window().then((win) => { + cy.stub(win, 'prompt').returns('DISABLED WINDOW PROMPT'); }); - }); + clickExpandActions(ALERT_TABLE_SEVERITY_VALUES, 0); + cy.get(CELL_COPY_BUTTON).should('exist'); + // We are not able to test the "copy to clipboard" action execution + // due to browsers security limitation accessing the clipboard services. + // We assume external `copy` library works + }); }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/alerts_details.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/alerts_details.cy.ts index 9be3268d02795..af17e1bad39f5 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/alerts_details.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/alerts_details.cy.ts @@ -26,7 +26,7 @@ import { getUnmappedRule } from '../../objects/rule'; import { ALERTS_URL } from '../../urls/navigation'; import { tablePageSelector } from '../../screens/table_pagination'; -describe('Alert details with unmapped fields', () => { +describe('Alert details with unmapped fields', { testIsolation: false }, () => { before(() => { cleanKibana(); esArchiverLoad('unmapped_fields'); @@ -37,17 +37,11 @@ describe('Alert details with unmapped fields', () => { expandFirstAlert(); }); - beforeEach(() => { - visitWithoutDateRange(ALERTS_URL); - waitForAlertsToPopulate(); - expandFirstAlert(); - }); - after(() => { esArchiverUnload('unmapped_fields'); }); - it('Displays the unmapped field on the JSON view', () => { + it('should display the unmapped field on the JSON view', () => { const expectedUnmappedValue = 'This is the unmapped field'; openJsonView(); @@ -58,7 +52,7 @@ describe('Alert details with unmapped fields', () => { }); }); - it('Displays the unmapped field on the table', () => { + it('should displays the unmapped field on the table', () => { const expectedUnmappedField = { field: 'unmapped', text: 'This is the unmapped field', @@ -76,7 +70,7 @@ describe('Alert details with unmapped fields', () => { }); // This test makes sure that the table does not overflow horizontally - it('Table does not scroll horizontally', () => { + it('table should not scroll horizontally', () => { openTable(); cy.get(ALERT_FLYOUT) diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/investigate_in_timeline.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/investigate_in_timeline.cy.ts index 5f52041e75d17..15052359a54a9 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/investigate_in_timeline.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/investigate_in_timeline.cy.ts @@ -5,36 +5,91 @@ * 2.0. */ +import { closeTimeline } from '../../tasks/timeline'; import { getNewRule } from '../../objects/rule'; -import { PROVIDER_BADGE } from '../../screens/timeline'; +import { PROVIDER_BADGE, QUERY_TAB_BUTTON, TIMELINE_TITLE } from '../../screens/timeline'; -import { investigateFirstAlertInTimeline } from '../../tasks/alerts'; +import { expandFirstAlert, investigateFirstAlertInTimeline } from '../../tasks/alerts'; import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; import { cleanKibana } from '../../tasks/common'; import { waitForAlertsToPopulate } from '../../tasks/create_new_rule'; import { login, visit } from '../../tasks/login'; import { ALERTS_URL } from '../../urls/navigation'; +import { + ALERT_FLYOUT, + INSIGHTS_INVESTIGATE_ANCESTRY_ALERTS_IN_TIMELINE_BUTTON, + INSIGHTS_INVESTIGATE_IN_TIMELINE_BUTTON, + INSIGHTS_RELATED_ALERTS_BY_ANCESTRY, + INSIGHTS_RELATED_ALERTS_BY_SESSION, + SUMMARY_VIEW_INVESTIGATE_IN_TIMELINE_BUTTON, + SUMMARY_VIEW_PREVALENCE_CELL, +} from '../../screens/alerts_details'; +import { verifyInsightCount } from '../../tasks/alerts_details'; -describe('Alerts timeline', () => { +describe('Investigate in timeline', { testIsolation: false }, () => { before(() => { cleanKibana(); login(); createCustomRuleEnabled(getNewRule()); - }); - beforeEach(() => { visit(ALERTS_URL); waitForAlertsToPopulate(); }); - it('Investigate alert in default timeline', () => { - investigateFirstAlertInTimeline(); - cy.get(PROVIDER_BADGE) - .first() - .invoke('text') - .then((eventId) => { - investigateFirstAlertInTimeline(); - cy.get(PROVIDER_BADGE).filter(':visible').should('have.text', eventId); + describe('From alerts table', () => { + after(() => { + closeTimeline(); + }); + + it('should open new timeline from alerts table', () => { + investigateFirstAlertInTimeline(); + cy.get(PROVIDER_BADGE) + .first() + .invoke('text') + .then((eventId) => { + investigateFirstAlertInTimeline(); + cy.get(PROVIDER_BADGE).filter(':visible').should('have.text', eventId); + }); + }); + }); + + describe('From alerts details flyout', () => { + before(() => { + expandFirstAlert(); + }); + + afterEach(() => { + closeTimeline(); + }); + + it('should open a new timeline from a prevalence field', () => { + cy.get(SUMMARY_VIEW_PREVALENCE_CELL) + .first() + .invoke('text') + .then((alertCount) => { + // Click on the first button that lets us investigate in timeline + cy.get(ALERT_FLYOUT).find(SUMMARY_VIEW_INVESTIGATE_IN_TIMELINE_BUTTON).first().click(); + + // Make sure a new timeline is created and opened + cy.get(TIMELINE_TITLE).should('contain.text', 'Untitled timeline'); + + // The alert count in this timeline should match the count shown on the alert flyout + cy.get(QUERY_TAB_BUTTON).should('contain.text', alertCount); + }); + }); + + it('should open a new timeline from an insights module', () => { + verifyInsightCount({ + tableSelector: INSIGHTS_RELATED_ALERTS_BY_SESSION, + investigateSelector: INSIGHTS_INVESTIGATE_IN_TIMELINE_BUTTON, + }); + }); + + it('should open a new timeline with alert ids from the process ancestry', () => { + verifyInsightCount({ + tableSelector: INSIGHTS_RELATED_ALERTS_BY_ANCESTRY, + investigateSelector: INSIGHTS_INVESTIGATE_ANCESTRY_ALERTS_IN_TIMELINE_BUTTON, }); + }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alert_details/navigation.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/navigation.cy.ts similarity index 100% rename from x-pack/plugins/security_solution/cypress/e2e/detection_alert_details/navigation.cy.ts rename to x-pack/plugins/security_solution/cypress/e2e/detection_alerts/navigation.cy.ts diff --git a/x-pack/plugins/security_solution/cypress/screens/alerts.ts b/x-pack/plugins/security_solution/cypress/screens/alerts.ts index fe78833ae76de..adc11f627fc44 100644 --- a/x-pack/plugins/security_solution/cypress/screens/alerts.ts +++ b/x-pack/plugins/security_solution/cypress/screens/alerts.ts @@ -137,6 +137,8 @@ export const EVENT_CONTAINER_TABLE_NOT_LOADING = export const FILTER_BADGE = '[data-test-subj^="filter-badge"]'; +export const FILTER_BADGE_DELETE = '[data-test-subj="deleteFilter"]'; + export const CELL_FILTER_IN_BUTTON = '[data-test-subj="dataGridColumnCellAction-security-default-cellActions-filterIn"]'; export const CELL_FILTER_OUT_BUTTON = @@ -153,6 +155,8 @@ export const ACTIONS_EXPAND_BUTTON = '[data-test-subj="euiDataGridCellExpandButt export const SHOW_TOP_N_HEADER = '[data-test-subj="topN-container"] [data-test-subj="header-section-title"]'; +export const SHOW_TOP_N_CLOSE_BUTTON = '[data-test-subj="close"]'; + export const ALERTS_HISTOGRAM_LEGEND = '[data-test-subj="alerts-histogram-panel"] [data-test-subj="withHoverActionsButton"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/timeline.ts b/x-pack/plugins/security_solution/cypress/screens/timeline.ts index 1fbe6e7fcaf60..0db357f3a7b7d 100644 --- a/x-pack/plugins/security_solution/cypress/screens/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/screens/timeline.ts @@ -112,6 +112,8 @@ export const PROCESS_KPI = '[data-test-subj="siem-timeline-process-kpi"'; export const PROVIDER_BADGE = '[data-test-subj="providerBadge"]'; +export const PROVIDER_BADGE_DELETE = '.delete-data-provider'; + export const RESET_FIELDS = '[data-test-subj="fields-browser-container"] [data-test-subj="reset-fields"]'; @@ -295,8 +297,12 @@ export const EDIT_TIMELINE_TOOLTIP = '[data-test-subj="save-timeline-btn-tooltip export const ALERT_TABLE_SEVERITY_VALUES = '[data-test-subj="formatted-field-kibana.alert.severity"]'; +export const ALERT_TABLE_ACTIONS_HEADER = '[data-gridcell-column-id="expandColumn"]'; + export const ALERT_TABLE_FILE_NAME_HEADER = '[data-gridcell-column-id="file.name"]'; +export const ALERT_TABLE_SEVERITY_HEADER = '[data-gridcell-column-id="kibana.alert.severity"]'; + export const ALERT_TABLE_FILE_NAME_VALUES = '[data-gridcell-column-id="file.name"][data-test-subj="dataGridRowCell"]'; // empty column for the test data diff --git a/x-pack/plugins/security_solution/cypress/tasks/alerts.ts b/x-pack/plugins/security_solution/cypress/tasks/alerts.ts index fbea06c251d5b..8de811b6201ad 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/alerts.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/alerts.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { TOP_N_CONTAINER } from '../screens/network/flows'; import { ADD_EXCEPTION_BTN, ALERT_CHECKBOX, @@ -37,6 +38,7 @@ import { ACTIONS_EXPAND_BUTTON, SELECT_HISTOGRAM, CELL_FILTER_OUT_BUTTON, + SHOW_TOP_N_CLOSE_BUTTON, } from '../screens/alerts'; import { LOADING_INDICATOR, REFRESH_BUTTON } from '../screens/security_header'; import { TIMELINE_COLUMN_SPINNER } from '../screens/timeline'; @@ -332,6 +334,11 @@ export const showTopNAlertProperty = (propertySelector: string, rowIndex: number clickExpandActions(propertySelector, rowIndex); cy.get(CELL_SHOW_TOP_FIELD_BUTTON).first().click({ force: true }); }; +export const closeTopNAlertProperty = () => { + cy.get(TOP_N_CONTAINER).then(() => { + cy.get(SHOW_TOP_N_CLOSE_BUTTON).click(); + }); +}; export const waitForAlerts = () => { /* diff --git a/x-pack/plugins/security_solution/cypress/tasks/configure_cases.ts b/x-pack/plugins/security_solution/cypress/tasks/configure_cases.ts index 4019468ddc43e..0b2f722204f5f 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/configure_cases.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/configure_cases.ts @@ -39,6 +39,10 @@ export const openAddNewConnectorOption = () => { }); }; +export const verifyNewConnectorSelected = (connector: Connector) => { + cy.get(CONNECTORS_DROPDOWN).should('have.text', connector.connectorName); +}; + export const selectLastConnectorCreated = (id: string) => { cy.get(CONNECTORS_DROPDOWN).click({ force: true }); cy.get(CONNECTOR(id)).click(); diff --git a/x-pack/plugins/security_solution/cypress/tasks/search_bar.ts b/x-pack/plugins/security_solution/cypress/tasks/search_bar.ts index fa3dbbc2589e5..2736b931ba0cf 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/search_bar.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/search_bar.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { FILTER_BADGE, FILTER_BADGE_DELETE } from '../screens/alerts'; import type { SearchBarFilter } from '../objects/filter'; import { @@ -34,6 +35,18 @@ export const fillKqlQueryBar = (query: string) => { cy.get(GLOBAL_KQL_INPUT).type(query); }; +export const clearKqlQueryBar = () => { + cy.get(GLOBAL_KQL_INPUT).should('be.visible'); + cy.get(GLOBAL_KQL_INPUT).clear(); +}; + +export const removeKqlFilter = () => { + cy.get(FILTER_BADGE).then((el) => { + el.click(); + cy.get(FILTER_BADGE_DELETE).click(); + }); +}; + export const fillAddFilterForm = ({ key, value, operator }: SearchBarFilter) => { cy.get(ADD_FILTER_FORM_FIELD_INPUT).should('exist'); cy.get(ADD_FILTER_FORM_FIELD_INPUT).should('be.visible'); diff --git a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts index 78a4fcb0cf7b1..45b7d4a526dee 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts @@ -79,6 +79,8 @@ import { TIMELINE_LUCENELANGUAGE_BUTTON, TIMELINE_KQLLANGUAGE_BUTTON, TIMELINE_QUERY, + PROVIDER_BADGE, + PROVIDER_BADGE_DELETE, } from '../screens/timeline'; import { REFRESH_BUTTON, TIMELINE } from '../screens/timelines'; import { drag, drop } from './common'; @@ -292,6 +294,12 @@ export const closeTimeline = () => { .should('not.be.visible'); }; +export const removeDataProvider = () => { + cy.get(PROVIDER_BADGE) + .click() + .then(() => cy.get(PROVIDER_BADGE_DELETE).click()); +}; + export const createNewTimeline = () => { cy.get(TIMELINE_SETTINGS_ICON) .filter(':visible') diff --git a/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx b/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx index f194ef0e463eb..bda2e4d8d3629 100644 --- a/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx +++ b/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx @@ -11,6 +11,7 @@ import { EuiThemeProvider, useEuiTheme } from '@elastic/eui'; import { IS_DRAGGING_CLASS_NAME } from '@kbn/securitysolution-t-grid'; import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; import type { KibanaPageTemplateProps } from '@kbn/shared-ux-page-kibana-template'; +import { ExpandableFlyout, ExpandableFlyoutProvider } from '@kbn/expandable-flyout'; import { useSecuritySolutionNavigation } from '../../../common/components/navigation/use_security_solution_navigation'; import { TimelineId } from '../../../../common/types/timeline'; import { getTimelineShowStatusByIdSelector } from '../../../timelines/components/flyout/selectors'; @@ -80,34 +81,35 @@ export const SecuritySolutionTemplateWrapper: React.FC - - - + - {children} - - - {isTimelineBottomBarVisible && ( - - - - - - )} - + + + {children} + + {isTimelineBottomBarVisible && ( + + + + + + )} + {}} /> + + ); }); diff --git a/x-pack/plugins/security_solution/public/common/components/control_columns/row_action/index.tsx b/x-pack/plugins/security_solution/public/common/components/control_columns/row_action/index.tsx index c41eaba862c9c..a7b3395c12c5a 100644 --- a/x-pack/plugins/security_solution/public/common/components/control_columns/row_action/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/control_columns/row_action/index.tsx @@ -8,6 +8,7 @@ import type { EuiDataGridCellValueElementProps } from '@elastic/eui'; import React, { useCallback, useMemo } from 'react'; import { useDispatch } from 'react-redux'; +import { useExpandableFlyoutContext } from '@kbn/expandable-flyout'; import type { SetEventsDeleted, SetEventsLoading, @@ -19,6 +20,7 @@ import { getMappedNonEcsValue } from '../../../../timelines/components/timeline/ import type { TimelineItem, TimelineNonEcsData } from '../../../../../common/search_strategy'; import type { ColumnHeaderOptions, OnRowSelected } from '../../../../../common/types/timeline'; import { dataTableActions } from '../../../store/data_table'; +import { useIsExperimentalFeatureEnabled } from '../../../hooks/use_experimental_features'; type Props = EuiDataGridCellValueElementProps & { columnHeaders: ColumnHeaderOptions[]; @@ -64,7 +66,10 @@ const RowActionComponent = ({ }: Props) => { const { data: timelineNonEcsData, ecs: ecsData, _id: eventId, _index: indexName } = data ?? {}; + const { openFlyout } = useExpandableFlyoutContext(); + const dispatch = useDispatch(); + const isSecurityFlyoutEnabled = useIsExperimentalFeatureEnabled('securityFlyoutEnabled'); const columnValues = useMemo( () => @@ -90,14 +95,18 @@ const RowActionComponent = ({ }, }; - dispatch( - dataTableActions.toggleDetailPanel({ - ...updatedExpandedDetail, - tabType, - id: tableId, - }) - ); - }, [dispatch, eventId, indexName, tabType, tableId]); + if (isSecurityFlyoutEnabled) { + openFlyout({}); + } else { + dispatch( + dataTableActions.toggleDetailPanel({ + ...updatedExpandedDetail, + tabType, + id: tableId, + }) + ); + } + }, [dispatch, eventId, indexName, isSecurityFlyoutEnabled, openFlyout, tabType, tableId]); const Action = controlColumn.rowCellRender; diff --git a/x-pack/plugins/security_solution/public/common/components/error_toast_dispatcher/index.tsx b/x-pack/plugins/security_solution/public/common/components/error_toast_dispatcher/index.tsx index 12f51c78fedab..0a441904a1f6b 100644 --- a/x-pack/plugins/security_solution/public/common/components/error_toast_dispatcher/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/error_toast_dispatcher/index.tsx @@ -31,7 +31,7 @@ const ErrorToastDispatcherComponent: React.FC = ({ toastLifeTimeMs = 5 toast: { color: 'danger', id, - iconType: 'alert', + iconType: 'error', title, errors: message, toastLifeTimeMs, diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_process_ancestry.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_process_ancestry.tsx index 99c842cd48810..43807363dda1f 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_process_ancestry.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_process_ancestry.tsx @@ -6,7 +6,7 @@ */ import React, { useMemo, useCallback, useEffect, useState } from 'react'; -import { EuiBetaBadge, EuiSpacer, EuiLoadingSpinner } from '@elastic/eui'; +import { EuiSpacer, EuiLoadingSpinner } from '@elastic/eui'; import type { Filter } from '@kbn/es-query'; import { isActiveTimeline } from '../../../../helpers'; @@ -25,7 +25,6 @@ import { PROCESS_ANCESTRY_ERROR, PROCESS_ANCESTRY_FILTER, } from './translations'; -import { BETA } from '../../../translations'; interface Props { data: TimelineEventsDetailsItem; @@ -102,8 +101,6 @@ export const RelatedAlertsByProcessAncestry = React.memo( ); }, [showContent, cache.alertIds, data, index, originalDocumentId, eventId, scopeId]); - const betaBadge = useMemo(() => , []); - return ( ( } renderContent={renderContent} onToggle={onToggle} - extraAction={betaBadge} /> ); } diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/index.tsx b/x-pack/plugins/security_solution/public/common/components/filter_group/index.tsx index 5e918b7c4d46d..be736952a5d34 100644 --- a/x-pack/plugins/security_solution/public/common/components/filter_group/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/index.tsx @@ -277,7 +277,7 @@ const FilterGroupComponent = (props: PropsWithChildren) => { }); }); - return initialInput; + return { initialInput }; }, [dataViewId, timeRange, filters, chainingSystem, query, selectControlsWithPriority] ); @@ -336,7 +336,7 @@ const FilterGroupComponent = (props: PropsWithChildren) => { {!controlGroup ? : null} diff --git a/x-pack/plugins/security_solution/public/common/components/grouping/accordion_panel/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/grouping/accordion_panel/index.test.tsx index 3da0aefb35895..db78f6f2e9257 100644 --- a/x-pack/plugins/security_solution/public/common/components/grouping/accordion_panel/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/grouping/accordion_panel/index.test.tsx @@ -16,6 +16,7 @@ const ruleName = 'Rule name'; const ruleDesc = 'Rule description'; const testProps = { + isLoading: false, groupBucket: { key: [ruleName, ruleDesc], key_as_string: `${ruleName}|${ruleDesc}`, diff --git a/x-pack/plugins/security_solution/public/common/components/grouping/accordion_panel/index.tsx b/x-pack/plugins/security_solution/public/common/components/grouping/accordion_panel/index.tsx index 446b872f3d5f1..548aa81272cd9 100644 --- a/x-pack/plugins/security_solution/public/common/components/grouping/accordion_panel/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/grouping/accordion_panel/index.tsx @@ -31,6 +31,7 @@ interface GroupPanelProps { forceState?: 'open' | 'closed'; groupBucket: RawBucket; groupPanelRenderer?: JSX.Element; + isLoading: boolean; level?: number; onToggleGroup?: (isOpen: boolean, groupBucket: RawBucket) => void; renderChildComponent: (groupFilter: Filter[]) => React.ReactNode; @@ -56,6 +57,7 @@ const GroupPanelComponent = ({ forceState, groupBucket, groupPanelRenderer, + isLoading, level = 0, onToggleGroup, renderChildComponent, @@ -89,6 +91,7 @@ const GroupPanelComponent = ({ data-test-subj="grouping-accordion" extraAction={extraAction} forceState={forceState} + isLoading={isLoading} id={`group${level}-${groupFieldValue}`} onToggle={onToggle} paddingSize="m" diff --git a/x-pack/plugins/security_solution/public/common/components/grouping/container/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/grouping/container/index.test.tsx index f51b722af2dec..d9aa2f589c343 100644 --- a/x-pack/plugins/security_solution/public/common/components/grouping/container/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/grouping/container/index.test.tsx @@ -94,16 +94,10 @@ const testProps = { ], }, alertsCount: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'siem', - doc_count: 2, - }, - ], + value: 2, }, }, + isLoading: false, pagination: { pageIndex: 0, pageSize: 25, @@ -142,9 +136,7 @@ describe('grouping container', () => { buckets: [], }, alertsCount: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [], + value: 0, }, }; const { getByTestId, queryByTestId } = render( diff --git a/x-pack/plugins/security_solution/public/common/components/grouping/container/index.tsx b/x-pack/plugins/security_solution/public/common/components/grouping/container/index.tsx index beb1ee796827a..9956ba8e40c06 100644 --- a/x-pack/plugins/security_solution/public/common/components/grouping/container/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/grouping/container/index.tsx @@ -5,7 +5,13 @@ * 2.0. */ -import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTablePagination } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiProgress, + EuiSpacer, + EuiTablePagination, +} from '@elastic/eui'; import type { Filter } from '@kbn/es-query'; import React, { useMemo, useState } from 'react'; import { firstNonNullValue } from '../../../../../common/endpoint/models/ecs_safety_helpers'; @@ -36,6 +42,7 @@ export interface GroupingContainerProps { groupPanelRenderer?: (fieldBucket: RawBucket) => JSX.Element | undefined; groupsSelector?: JSX.Element; inspectButton?: JSX.Element; + isLoading: boolean; pagination: { pageIndex: number; pageSize: number; @@ -55,6 +62,7 @@ const GroupingContainerComponent = ({ groupPanelRenderer, groupsSelector, inspectButton, + isLoading, pagination, renderChildComponent, selectedGroup, @@ -67,12 +75,9 @@ const GroupingContainerComponent = ({ const groupsNumber = data?.groupsNumber?.value ?? 0; const unitCountText = useMemo(() => { - const count = - data?.alertsCount?.buckets && data?.alertsCount?.buckets.length > 0 - ? data?.alertsCount?.buckets[0].doc_count ?? 0 - : 0; + const count = data?.alertsCount?.value ?? 0; return `${count.toLocaleString()} ${unit && unit(count)}`; - }, [data?.alertsCount?.buckets, unit]); + }, [data?.alertsCount?.value, unit]); const unitGroupsCountText = useMemo( () => `${groupsNumber.toLocaleString()} ${GROUPS_UNIT(groupsNumber)}`, @@ -99,6 +104,7 @@ const GroupingContainerComponent = ({ forceState={(trigger[groupKey] && trigger[groupKey].state) ?? 'closed'} groupBucket={groupBucket} groupPanelRenderer={groupPanelRenderer && groupPanelRenderer(groupBucket)} + isLoading={isLoading} onToggleGroup={(isOpen) => { setTrigger({ // ...trigger, -> this change will keep only one group at a time expanded and one table displayed @@ -124,6 +130,7 @@ const GroupingContainerComponent = ({ customMetricStats, data.stackByMultipleFields0?.buckets, groupPanelRenderer, + isLoading, renderChildComponent, selectedGroup, takeActionItems, @@ -137,6 +144,7 @@ const GroupingContainerComponent = ({ return ( <> ) : ( - + <> + {isLoading && ( + + )} + + )} diff --git a/x-pack/plugins/security_solution/public/common/components/grouping/groups_selector/index.tsx b/x-pack/plugins/security_solution/public/common/components/grouping/groups_selector/index.tsx index 389645bf549da..81382406d2af4 100644 --- a/x-pack/plugins/security_solution/public/common/components/grouping/groups_selector/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/grouping/groups_selector/index.tsx @@ -9,18 +9,13 @@ import type { EuiContextMenuPanelDescriptor, EuiContextMenuPanelItemDescriptor, } from '@elastic/eui'; -import { EuiFlexGroup, EuiFlexItem, EuiBetaBadge, EuiPopover } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; +import { EuiBetaBadge, EuiFlexGroup, EuiFlexItem, EuiPopover } from '@elastic/eui'; import React, { useCallback, useMemo, useState } from 'react'; import type { FieldSpec } from '@kbn/data-views-plugin/common'; import { CustomFieldPanel } from './custom_field_panel'; -import { GROUP_BY, TECHNICAL_PREVIEW } from '../translations'; +import * as i18n from '../translations'; import { StyledContextMenu, StyledEuiButtonEmpty } from '../styles'; -const none = i18n.translate('xpack.securitySolution.groupsSelector.noneGroupByOptionName', { - defaultMessage: 'None', -}); - interface GroupSelectorProps { fields: FieldSpec[]; groupSelected: string; @@ -34,17 +29,38 @@ const GroupsSelectorComponent = ({ groupSelected = 'none', onGroupChange, options, - title = '', + title = i18n.GROUP_BY, }: GroupSelectorProps) => { const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const panels: EuiContextMenuPanelDescriptor[] = useMemo( () => [ { id: 'firstPanel', + title: ( + + + {i18n.SELECT_FIELD.toUpperCase()} + + + + + + ), items: [ { 'data-test-subj': 'panel-none', - name: none, + name: i18n.NONE, icon: groupSelected === 'none' ? 'check' : 'empty', onClick: () => onGroupChange('none'), }, @@ -56,9 +72,7 @@ const GroupsSelectorComponent = ({ })), { 'data-test-subj': `panel-custom`, - name: i18n.translate('xpack.securitySolution.groupsSelector.customGroupByOptionName', { - defaultMessage: 'Custom field', - }), + name: i18n.CUSTOM_FIELD, icon: 'empty', panel: 'customPanel', }, @@ -66,9 +80,7 @@ const GroupsSelectorComponent = ({ }, { id: 'customPanel', - title: i18n.translate('xpack.securitySolution.groupsSelector.customGroupByPanelTitle', { - defaultMessage: 'Group By Custom Field', - }), + title: i18n.GROUP_BY_CUSTOM_FIELD, width: 685, content: ( 0 ? selectedOption[0].label : none + groupSelected !== 'none' && selectedOption.length > 0 + ? selectedOption[0].label + : i18n.NONE } size="xs" > - {`${title ?? GROUP_BY}: ${ - groupSelected !== 'none' && selectedOption.length > 0 ? selectedOption[0].label : none + {`${title}: ${ + groupSelected !== 'none' && selectedOption.length > 0 + ? selectedOption[0].label + : i18n.NONE }`} ), @@ -114,25 +130,18 @@ const GroupsSelectorComponent = ({ ); return ( - - - - - - - - - - + + + ); }; diff --git a/x-pack/plugins/security_solution/public/common/components/grouping/query/index.ts b/x-pack/plugins/security_solution/public/common/components/grouping/query/index.ts index 5abd10f1c2575..8a0242e8cc3ae 100644 --- a/x-pack/plugins/security_solution/public/common/components/grouping/query/index.ts +++ b/x-pack/plugins/security_solution/public/common/components/grouping/query/index.ts @@ -6,12 +6,7 @@ */ import { isEmpty } from 'lodash/fp'; -import type { - GroupingQueryArgs, - GroupingQuery, - SubAggregation, - TermsOrCardinalityAggregation, -} from './types'; +import type { GroupingQueryArgs, GroupingQuery, NamedAggregation } from './types'; /** The maximum number of items to render */ export const DEFAULT_STACK_BY_FIELD0_SIZE = 10; export const DEFAULT_STACK_BY_FIELD1_SIZE = 10; @@ -27,8 +22,8 @@ const getOptionalSubAggregation = ({ stackByMultipleFields1Size: number; stackByMultipleFields1From?: number; stackByMultipleFields1Sort?: Array<{ [category: string]: { order: 'asc' | 'desc' } }>; - additionalStatsAggregationsFields1: TermsOrCardinalityAggregation[]; -}): SubAggregation | {} => + additionalStatsAggregationsFields1: NamedAggregation[]; +}): NamedAggregation | {} => stackByMultipleFields1 != null && !isEmpty(stackByMultipleFields1) ? { stackByMultipleFields1: { diff --git a/x-pack/plugins/security_solution/public/common/components/grouping/query/types.ts b/x-pack/plugins/security_solution/public/common/components/grouping/query/types.ts index 853806a681161..4f1eb464e6fa6 100644 --- a/x-pack/plugins/security_solution/public/common/components/grouping/query/types.ts +++ b/x-pack/plugins/security_solution/public/common/components/grouping/query/types.ts @@ -17,43 +17,31 @@ interface RangeAgg { range: { '@timestamp': { gte: string; lte: string } }; } -export interface TermsOrCardinalityAggregation { - [category: string]: - | { - cardinality: estypes.AggregationsAggregationContainer['cardinality']; - } - | { - terms: estypes.AggregationsAggregationContainer['terms']; - }; -} +export type NamedAggregation = Record; export interface GroupingQueryArgs { additionalFilters: BoolAgg[]; from: string; runtimeMappings?: MappingRuntimeFields; - additionalAggregationsRoot?: TermsOrCardinalityAggregation[]; + additionalAggregationsRoot?: NamedAggregation[]; stackByMultipleFields0: string[]; stackByMultipleFields0Size?: number; stackByMultipleFields0From?: number; stackByMultipleFields0Sort?: Array<{ [category: string]: { order: 'asc' | 'desc' } }>; - additionalStatsAggregationsFields0: TermsOrCardinalityAggregation[]; + additionalStatsAggregationsFields0: NamedAggregation[]; stackByMultipleFields1: string[] | undefined; stackByMultipleFields1Size?: number; stackByMultipleFields1From?: number; stackByMultipleFields1Sort?: Array<{ [category: string]: { order: estypes.SortOrder } }>; - additionalStatsAggregationsFields1: TermsOrCardinalityAggregation[]; + additionalStatsAggregationsFields1: NamedAggregation[]; to: string; } -export interface SubAggregation extends Record { - bucket_truncate: { bucket_sort: estypes.AggregationsAggregationContainer['bucket_sort'] }; -} - -export interface MainAggregation extends Record { +export interface MainAggregation extends NamedAggregation { stackByMultipleFields0: { terms?: estypes.AggregationsAggregationContainer['terms']; multi_terms?: estypes.AggregationsAggregationContainer['multi_terms']; - aggs: SubAggregation; + aggs: NamedAggregation; }; } diff --git a/x-pack/plugins/security_solution/public/common/components/grouping/translations.ts b/x-pack/plugins/security_solution/public/common/components/grouping/translations.ts index 2e73e95b80de5..a9cd9a7d233dc 100644 --- a/x-pack/plugins/security_solution/public/common/components/grouping/translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/grouping/translations.ts @@ -20,13 +20,40 @@ export const TAKE_ACTION = i18n.translate( } ); -export const TECHNICAL_PREVIEW = i18n.translate( - 'xpack.securitySolution.grouping.technicalPreviewLabel', +export const BETA = i18n.translate('xpack.securitySolution.grouping.betaLabel', { + defaultMessage: 'Beta', +}); + +export const BETA_TOOL_TIP = i18n.translate('xpack.securitySolution.grouping.betaToolTip', { + defaultMessage: + 'Grouping may show only a subset of alerts while in beta. To see all alerts, use the list view by selecting "None"', +}); + +export const GROUP_BY = i18n.translate('xpack.securitySolution.selector.grouping.label', { + defaultMessage: 'Group alerts by', +}); + +export const GROUP_BY_CUSTOM_FIELD = i18n.translate( + 'xpack.securitySolution.groupsSelector.customGroupByPanelTitle', { - defaultMessage: 'Technical Preview', + defaultMessage: 'Group By Custom Field', } ); -export const GROUP_BY = i18n.translate('xpack.securitySolution.selector.grouping.label', { - defaultMessage: 'Group by field', +export const SELECT_FIELD = i18n.translate( + 'xpack.securitySolution.groupsSelector.groupByPanelTitle', + { + defaultMessage: 'Select Field', + } +); + +export const NONE = i18n.translate('xpack.securitySolution.groupsSelector.noneGroupByOptionName', { + defaultMessage: 'None', }); + +export const CUSTOM_FIELD = i18n.translate( + 'xpack.securitySolution.groupsSelector.customGroupByOptionName', + { + defaultMessage: 'Custom field', + } +); diff --git a/x-pack/plugins/security_solution/public/common/components/toasters/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/toasters/index.test.tsx index 17a2ebe192a44..15178cc72b85a 100644 --- a/x-pack/plugins/security_solution/public/common/components/toasters/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/toasters/index.test.tsx @@ -299,7 +299,7 @@ describe('Toaster', () => { toast: { color: 'danger', errors: ['message'], - iconType: 'alert', + iconType: 'error', id: '9e1f72a9-7c73-4b7f-a562-09940f7daf4a', title: 'Title', }, diff --git a/x-pack/plugins/security_solution/public/common/components/toasters/utils.test.ts b/x-pack/plugins/security_solution/public/common/components/toasters/utils.test.ts index 34871b2e68efa..8c660a5d0684a 100644 --- a/x-pack/plugins/security_solution/public/common/components/toasters/utils.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/toasters/utils.test.ts @@ -28,7 +28,7 @@ describe('error_to_toaster', () => { toast: { color: 'danger', errors: ['some error 1', 'some error 2'], - iconType: 'alert', + iconType: 'error', id: 'some-made-up-id', title: 'some title', }, @@ -45,7 +45,7 @@ describe('error_to_toaster', () => { toast: { color: 'danger', errors: ['some error 1'], - iconType: 'alert', + iconType: 'error', id: 'some-made-up-id', title: 'some title', }, @@ -64,7 +64,7 @@ describe('error_to_toaster', () => { toast: { color: 'danger', errors: ['something bad happened'], - iconType: 'alert', + iconType: 'error', id: 'some-made-up-id', title: 'some title', }, @@ -82,7 +82,7 @@ describe('error_to_toaster', () => { toast: { color: 'danger', errors: ['Internal Server Error'], - iconType: 'alert', + iconType: 'error', id: 'some-made-up-id', title: 'some title', }, @@ -99,7 +99,7 @@ describe('error_to_toaster', () => { toast: { color: 'danger', errors: ['some error 1'], - iconType: 'alert', + iconType: 'error', id: 'some-made-up-id', title: 'some title', }, @@ -116,7 +116,7 @@ describe('error_to_toaster', () => { toast: { color: 'danger', errors: ['Network Error'], - iconType: 'alert', + iconType: 'error', id: 'some-made-up-id', title: 'some title', }, diff --git a/x-pack/plugins/security_solution/public/common/components/toasters/utils.ts b/x-pack/plugins/security_solution/public/common/components/toasters/utils.ts index 5825f9ad18f06..0114cb2cf4a45 100644 --- a/x-pack/plugins/security_solution/public/common/components/toasters/utils.ts +++ b/x-pack/plugins/security_solution/public/common/components/toasters/utils.ts @@ -30,7 +30,7 @@ export const displayErrorToast = ( id, title: errorTitle, color: 'danger', - iconType: 'alert', + iconType: 'error', errors: errorMessages, }; dispatchToaster({ @@ -87,7 +87,7 @@ export const errorToToaster = ({ title, error, color = 'danger', - iconType = 'alert', + iconType = 'error', dispatchToaster, }: ErrorToToasterArgs) => { let toast: AppToast; diff --git a/x-pack/plugins/security_solution/public/common/containers/grouping/hooks/use_get_group_selector.tsx b/x-pack/plugins/security_solution/public/common/containers/grouping/hooks/use_get_group_selector.tsx index aac4305f8518c..0e29b3edba7ab 100644 --- a/x-pack/plugins/security_solution/public/common/containers/grouping/hooks/use_get_group_selector.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/grouping/hooks/use_get_group_selector.tsx @@ -8,6 +8,7 @@ import type { FieldSpec } from '@kbn/data-views-plugin/common'; import React, { useCallback, useEffect, useMemo } from 'react'; import { useDispatch, useSelector } from 'react-redux'; +import { GROUP_BY } from '../../../components/grouping/translations'; import type { TableId } from '../../../../../common/types'; import { getDefaultGroupingOptions } from '../../../../detections/components/alerts_table/grouping_settings'; import type { State } from '../../../store'; @@ -101,7 +102,7 @@ export const useGetGroupingSelector = ({ }} fields={fields} options={options} - title={'Group Alerts Selector'} + title={GROUP_BY} /> ), [ diff --git a/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx b/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx index cdccb9dc40edc..253c16a044f04 100644 --- a/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx +++ b/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx @@ -24,6 +24,7 @@ import type { } from '@testing-library/react-hooks/src/types/react'; import type { UseBaseQueryResult } from '@tanstack/react-query'; import ReactDOM from 'react-dom'; +import { ExperimentalFeaturesService } from '../../experimental_features_service'; import { applyIntersectionObserverMock } from '../intersection_observer_mock'; import { ConsoleManager } from '../../../management/components/console'; import type { StartPlugins, StartServices } from '../../../types'; @@ -42,6 +43,7 @@ import { APP_UI_ID, APP_PATH } from '../../../../common/constants'; import { KibanaContextProvider, KibanaServices } from '../../lib/kibana'; import { getDeepLinks } from '../../../app/deep_links'; import { fleetGetPackageHttpMock } from '../../../management/mocks'; +import { allowedExperimentalValues } from '../../../../common/experimental_features'; const REAL_REACT_DOM_CREATE_PORTAL = ReactDOM.createPortal; @@ -282,7 +284,16 @@ export const createAppRootMockRenderer = (): AppContextTestRender => { return hookResult.current; }; + ExperimentalFeaturesService.init({ experimentalFeatures: allowedExperimentalValues }); + const setExperimentalFlag: AppContextTestRender['setExperimentalFlag'] = (flags) => { + ExperimentalFeaturesService.init({ + experimentalFeatures: { + ...allowedExperimentalValues, + ...flags, + }, + }); + store.dispatch({ type: UpdateExperimentalFeaturesTestActionType, payload: flags, diff --git a/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx b/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx index 5761e98dbe366..d0901071063ab 100644 --- a/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx +++ b/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx @@ -20,6 +20,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import type { Action } from '@kbn/ui-actions-plugin/public'; import { CellActionsProvider } from '@kbn/cell-actions'; +import { ExpandableFlyoutProvider } from '@kbn/expandable-flyout'; import { ConsoleManager } from '../../management/components/console'; import type { State } from '../store'; import { createStore } from '../store'; @@ -66,13 +67,15 @@ export const TestProvidersComponent: React.FC = ({ ({ eui: euiDarkVars, darkMode: true })}> - - Promise.resolve(cellActions)} - > - {children} - - + + + Promise.resolve(cellActions)} + > + {children} + + + diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouped_alerts.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouped_alerts.tsx index 2e4576576dc60..6c5f68d546f97 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouped_alerts.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouped_alerts.tsx @@ -14,7 +14,6 @@ import { v4 as uuidv4 } from 'uuid'; import type { Filter } from '@kbn/es-query'; import { buildEsQuery } from '@kbn/es-query'; import { getEsQueryConfig } from '@kbn/data-plugin/common'; -import type { ReactNode } from 'react-markdown'; import { useGetGroupingSelector } from '../../../common/containers/grouping/hooks/use_get_group_selector'; import type { Status } from '../../../../common/detection_engine/schemas/common'; import { defaultGroup } from '../../../common/store/grouping/defaults'; @@ -68,10 +67,10 @@ interface OwnProps { runtimeMappings: MappingRuntimeFields; signalIndexName: string | null; currentAlertStatusFilterValue?: Status; - renderChildComponent: (groupingFilters: Filter[]) => ReactNode; + renderChildComponent: (groupingFilters: Filter[]) => React.ReactElement; } -type AlertsTableComponentProps = OwnProps & PropsFromRedux; +export type AlertsTableComponentProps = OwnProps & PropsFromRedux; export const GroupedAlertsTableComponent: React.FC = ({ defaultFilters = [], @@ -91,10 +90,10 @@ export const GroupedAlertsTableComponent: React.FC = const dispatch = useDispatch(); const groupingId = tableId; - const getGroupbyIdSelector = groupSelectors.getGroupByIdSelector(); + const getGroupByIdSelector = groupSelectors.getGroupByIdSelector(); const { activeGroup: selectedGroup } = - useSelector((state: State) => getGroupbyIdSelector(state, groupingId)) ?? defaultGroup; + useSelector((state: State) => getGroupByIdSelector(state, groupingId)) ?? defaultGroup; const { browserFields, @@ -237,41 +236,50 @@ export const GroupedAlertsTableComponent: React.FC = [defaultFilters, getGlobalQuery, takeActionItems] ); - if (loading || isLoadingGroups || isEmpty(selectedPatterns)) { + const groupedAlerts = useMemo( + () => + isNoneGroup(selectedGroup) ? ( + renderChildComponent([]) + ) : ( + + getSelectedGroupBadgeMetrics(selectedGroup, fieldBucket) + } + customMetricStats={(fieldBucket: RawBucket) => + getSelectedGroupCustomMetrics(selectedGroup, fieldBucket) + } + data={alertsGroupsData?.aggregations ?? {}} + groupPanelRenderer={(fieldBucket: RawBucket) => + getSelectedGroupButtonContent(selectedGroup, fieldBucket) + } + groupsSelector={groupsSelector} + inspectButton={inspect} + isLoading={loading || isLoadingGroups} + pagination={pagination} + renderChildComponent={renderChildComponent} + selectedGroup={selectedGroup} + takeActionItems={getTakeActionItems} + unit={defaultUnit} + /> + ), + [ + alertsGroupsData?.aggregations, + getTakeActionItems, + groupsSelector, + inspect, + isLoadingGroups, + loading, + pagination, + renderChildComponent, + selectedGroup, + ] + ); + + if (isEmpty(selectedPatterns)) { return null; } - const dataTable = renderChildComponent([]); - - return ( - <> - {isNoneGroup(selectedGroup) ? ( - dataTable - ) : ( - <> - - getSelectedGroupButtonContent(selectedGroup, fieldBucket) - } - badgeMetricStats={(fieldBucket: RawBucket) => - getSelectedGroupBadgeMetrics(selectedGroup, fieldBucket) - } - customMetricStats={(fieldBucket: RawBucket) => - getSelectedGroupCustomMetrics(selectedGroup, fieldBucket) - } - /> - - )} - - ); + return groupedAlerts; }; const makeMapStateToProps = () => { diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.test.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.test.ts index dc9674967a9fe..33f31d914e5a5 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.test.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.test.ts @@ -45,9 +45,8 @@ describe('getAlertsGroupingQuery', () => { _source: false, aggs: { alertsCount: { - terms: { - exclude: ['alerts'], - field: 'kibana.alert.rule.producer', + value_count: { + field: 'kibana.alert.rule.name', }, }, groupsNumber: { @@ -183,9 +182,8 @@ describe('getAlertsGroupingQuery', () => { _source: false, aggs: { alertsCount: { - terms: { - exclude: ['alerts'], - field: 'kibana.alert.rule.producer', + value_count: { + field: 'process.name', }, }, groupsNumber: { diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts index 52ae97ca1a512..ed20de72adab2 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts @@ -7,7 +7,7 @@ import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types'; import type { BoolQuery } from '@kbn/es-query'; -import type { TermsOrCardinalityAggregation } from '../../../../common/components/grouping'; +import type { NamedAggregation } from '../../../../common/components/grouping'; import { getGroupingQuery } from '../../../../common/components/grouping'; const getGroupFields = (groupValue: string) => { @@ -43,12 +43,7 @@ export const getAlertsGroupingQuery = ({ additionalFilters, additionalAggregationsRoot: [ { - alertsCount: { - terms: { - field: 'kibana.alert.rule.producer', - exclude: ['alerts'], - }, - }, + alertsCount: { value_count: { field: selectedGroup } }, }, ...(selectedGroup !== 'none' ? [ @@ -74,8 +69,8 @@ export const getAlertsGroupingQuery = ({ stackByMultipleFields1: [], }); -const getAggregationsByGroupField = (field: string): TermsOrCardinalityAggregation[] => { - const aggMetrics: TermsOrCardinalityAggregation[] = [ +const getAggregationsByGroupField = (field: string): NamedAggregation[] => { + const aggMetrics: NamedAggregation[] = [ { alertsCount: { cardinality: { diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.test.tsx index f2d1c860bf390..8cb24295f62a4 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.test.tsx @@ -6,9 +6,8 @@ */ import React from 'react'; -import { shallow } from 'enzyme'; import { waitFor, render, fireEvent } from '@testing-library/react'; -import type { Filter, Query } from '@kbn/es-query'; +import type { Filter } from '@kbn/es-query'; import useResizeObserver from 'use-resize-observer/polyfilled'; import '../../../common/mock/match_media'; @@ -19,6 +18,7 @@ import { SUB_PLUGINS_REDUCER, TestProviders, } from '../../../common/mock'; +import type { AlertsTableComponentProps } from './grouped_alerts'; import { GroupedAlertsTableComponent } from './grouped_alerts'; import { TableId } from '../../../../common/types'; import { useSourcererDataView } from '../../../common/containers/sourcerer'; @@ -28,8 +28,8 @@ import { mockTimelines } from '../../../common/mock/mock_timelines_plugin'; import { createFilterManagerMock } from '@kbn/data-plugin/public/query/filter_manager/filter_manager.mock'; import type { State } from '../../../common/store'; import { createStore } from '../../../common/store'; -import { AlertsTableComponent } from '.'; import { createStartServicesMock } from '../../../common/lib/kibana/kibana_react.mock'; +import { defaultGroup } from '../../../common/store/grouping/defaults'; jest.mock('../../../common/containers/sourcerer'); jest.mock('../../../common/containers/use_global_time', () => ({ @@ -149,6 +149,18 @@ const state: State = { const { storage } = createSecuritySolutionStorageMock(); const store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage); +const groupingStore = createStore( + { + ...state, + groups: { + groupById: { [`${TableId.test}`]: { ...defaultGroup, activeGroup: 'host.name' } }, + }, + }, + SUB_PLUGINS_REDUCER, + kibanaObservable, + storage +); + jest.mock('./timeline_actions/use_add_bulk_to_timeline', () => ({ useAddBulkToTimelineAction: jest.fn(() => {}), })); @@ -165,52 +177,64 @@ const sourcererDataView = { }, browserFields: {}, }; +const renderChildComponent = (groupingFilters: Filter[]) =>

    ; -const from = '2020-07-07T08:20:18.966Z'; -const to = '2020-07-08T08:20:18.966Z'; -const renderChildComponent = (groupingFilters: Filter[]) => ( - -); +const testProps: AlertsTableComponentProps = { + defaultFilters: [], + dispatch: jest.fn(), + from: '2020-07-07T08:20:18.966Z', + globalFilters: [], + globalQuery: { + query: 'query', + language: 'language', + }, + hasIndexMaintenance: true, + hasIndexWrite: true, + loading: false, + renderChildComponent, + runtimeMappings: {}, + signalIndexName: 'test', + tableId: TableId.test, + to: '2020-07-08T08:20:18.966Z', +}; describe('GroupedAlertsTable', () => { - (useSourcererDataView as jest.Mock).mockReturnValue({ - ...sourcererDataView, - selectedPatterns: ['myFakebeat-*'], + beforeEach(() => { + jest.clearAllMocks(); + (useSourcererDataView as jest.Mock).mockReturnValue({ + ...sourcererDataView, + selectedPatterns: ['myFakebeat-*'], + }); }); - it('renders correctly', () => { - const wrapper = shallow( + it('renders alerts table when no group selected', () => { + const { getByTestId, queryByTestId } = render( - + ); + expect(getByTestId('alerts-table')).toBeInTheDocument(); + expect(queryByTestId('grouping-table')).not.toBeInTheDocument(); + }); - expect(wrapper.find('[title="Alerts"]')).toBeTruthy(); + it('renders grouped alerts when group selected', () => { + const { getByTestId, queryByTestId } = render( + + + + ); + expect(getByTestId('grouping-table')).toBeInTheDocument(); + expect(queryByTestId('alerts-table')).not.toBeInTheDocument(); + expect(queryByTestId('is-loading-grouping-table')).not.toBeInTheDocument(); + }); + + it('renders loading when expected', () => { + const { getByTestId } = render( + + + + ); + expect(getByTestId('is-loading-grouping-table')).toBeInTheDocument(); }); // Not a valid test as of now.. because, table is used from trigger actions.. diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx index 1b122df170bec..8857f3ca3b079 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx @@ -282,8 +282,8 @@ export const AlertsTableComponent: FC = ({ id: tableId, title: i18n.SESSIONS_TITLE, defaultColumns: finalColumns.map((c) => ({ - ...c, initialWidth: DEFAULT_COLUMN_MIN_WIDTH, + ...c, })), }) ); diff --git a/x-pack/plugins/security_solution/public/detections/components/host_isolation/isolate.tsx b/x-pack/plugins/security_solution/public/detections/components/host_isolation/isolate.tsx index 1b47ec16714f8..044e893572151 100644 --- a/x-pack/plugins/security_solution/public/detections/components/host_isolation/isolate.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/host_isolation/isolate.tsx @@ -38,7 +38,11 @@ export const IsolateHost = React.memo( return caseInfo.id; }); - const { loading, isolateHost } = useHostIsolation({ endpointId, comment, caseIds }); + const { loading, isolateHost } = useHostIsolation({ + endpointId, + comment, + caseIds: caseIds.length > 0 ? caseIds : undefined, + }); const confirmHostIsolation = useCallback(async () => { const hostIsolated = await isolateHost(); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/translations.ts b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/translations.ts index bd73aa2b3a27d..4ab9328ee806b 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/translations.ts @@ -58,6 +58,13 @@ export const QUERY_PREVIEW_SELECT_ARIA = i18n.translate( } ); +export const RULE_PREVIEW_ERROR = i18n.translate( + 'xpack.securitySolution.detectionEngine.queryPreview.rulePreviewError', + { + defaultMessage: 'Failed to preview rule', + } +); + export const QUERY_PREVIEW_LABEL = i18n.translate( 'xpack.securitySolution.detectionEngine.queryPreview.queryPreviewLabel', { diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/use_preview_rule.ts b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/use_preview_rule.ts index 17d47008e77bc..aa9feabab32c1 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/use_preview_rule.ts +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/use_preview_rule.ts @@ -14,10 +14,10 @@ import type { } from '../../../../../common/detection_engine/rule_schema'; import { previewRule } from '../../../../detection_engine/rule_management/api/api'; -import * as i18n from '../../../../detection_engine/rule_management/logic/translations'; import { transformOutput } from '../../../containers/detection_engine/rules/transforms'; import type { TimeframePreviewOptions } from '../../../pages/detection_engine/rules/types'; import { usePreviewInvocationCount } from './use_preview_invocation_count'; +import * as i18n from './translations'; const emptyPreviewRule: PreviewResponse = { previewId: undefined, @@ -73,7 +73,7 @@ export const usePreviewRule = ({ } } catch (error) { if (isSubscribed) { - addError(error, { title: i18n.RULE_ADD_FAILURE }); + addError(error, { title: i18n.RULE_PREVIEW_ERROR }); } } if (isSubscribed) { diff --git a/x-pack/plugins/security_solution/public/management/components/console/components/command_list.test.tsx b/x-pack/plugins/security_solution/public/management/components/console/components/command_list.test.tsx new file mode 100644 index 0000000000000..7696fe035f2a0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/console/components/command_list.test.tsx @@ -0,0 +1,155 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ConsoleTestSetup, HelpSidePanelSelectorsAndActions } from '../mocks'; +import { + getCommandListMock, + getConsoleTestSetup, + getHelpSidePanelSelectorsAndActionsMock, +} from '../mocks'; +import React from 'react'; +import { waitFor } from '@testing-library/react'; + +describe('When rendering the command list (help output)', () => { + let render: ConsoleTestSetup['renderConsole']; + let renderResult: ReturnType; + let consoleSelectors: ConsoleTestSetup['selectors']; + let enterCommand: ConsoleTestSetup['enterCommand']; + + beforeEach(() => { + const testSetup = getConsoleTestSetup(); + + render = (props = {}) => (renderResult = testSetup.renderConsole(props)); + consoleSelectors = testSetup.selectors; + enterCommand = testSetup.enterCommand; + }); + + describe('and its displayed on the side panel', () => { + let renderAndOpenHelpPanel: typeof render; + let helpPanelSelectors: HelpSidePanelSelectorsAndActions; + + beforeEach(() => { + renderAndOpenHelpPanel = (props) => { + render(props); + helpPanelSelectors = getHelpSidePanelSelectorsAndActionsMock(renderResult); + consoleSelectors.openHelpPanel(); + + return renderResult; + }; + }); + + it('should display the help panel header', () => { + renderAndOpenHelpPanel(); + + expect(renderResult.getByTestId('test-sidePanel-header')).toHaveTextContent( + 'HelpUse the add () button to populate a response action to the text bar. Add ' + + 'additional parameters or comments as necessary.' + ); + }); + + it('should display the command list', () => { + renderAndOpenHelpPanel(); + + expect(renderResult.getByTestId('test-commandList')).toBeTruthy(); + }); + + it('should close the side panel when close button is clicked', () => { + renderAndOpenHelpPanel(); + consoleSelectors.closeHelpPanel(); + + expect(renderResult.queryByTestId('test-sidePanel')).toBeNull(); + }); + + it('should display helpful tips', () => { + renderAndOpenHelpPanel(); + + expect(renderResult.getByTestId('test-commandList-helpfulTips')).toHaveTextContent( + 'Helpful tips:You can enter consecutive response actions — no need to wait for previous ' + + 'actions to complete.Leaving the response console does not terminate any actions that have ' + + 'been submitted.Learn moreExternal link(opens in a new tab or window) about response actions ' + + 'and using the console.' + ); + + expect(renderResult.getByTestId('test-commandList-helpfulHintDocLink')).toBeTruthy(); + }); + + it('should display common commands and parameters section', () => { + renderAndOpenHelpPanel(); + + expect( + renderResult.getByTestId('test-commandList-Supportingcommandsparameters') + ).toBeTruthy(); + }); + + it('should group commands by group label', () => { + renderAndOpenHelpPanel(); + const groups = helpPanelSelectors.getHelpGroupLabels(); + + expect(groups).toEqual([ + 'group 1', + 'Supporting commands & parameters', + 'group 2', + 'Other commands', + ]); + }); + + it('should display the list of command in the expected order', () => { + renderAndOpenHelpPanel(); + const commands = helpPanelSelectors.getHelpCommandNames('group 1'); + + expect(commands).toEqual(['cmd6 --foo', 'cmd1']); + }); + + it('should hide command if command definition helpHidden is true', () => { + const commands = getCommandListMock(); + commands[0].helpHidden = true; + renderAndOpenHelpPanel({ commands }); + + expect(renderResult.queryByTestId('test-commandList-group1-cmd1')).toBeNull(); + }); + + it('should disable "add to text bar" button if command definition helpHidden is true', () => { + const commands = getCommandListMock(); + commands[0].helpDisabled = true; + renderAndOpenHelpPanel({ commands }); + + expect(renderResult.getByTestId('test-commandList-group1-cmd1-addToInput')).toBeDisabled(); + }); + + it('should add command to console input when [+] button is clicked', () => { + renderAndOpenHelpPanel(); + renderResult.getByTestId('test-commandList-group1-cmd6-addToInput').click(); + expect(consoleSelectors.getInputText()).toEqual('cmd6 --foo '); + }); + + it('should display custom help output when Command service has `getHelp()` defined', async () => { + const HelpComponent: React.FunctionComponent = () => { + return

    {'help output'}
    ; + }; + render({ HelpComponent }); + enterCommand('help'); + + await waitFor(() => { + expect(renderResult.getByTestId('custom-help')).toBeTruthy(); + }); + }); + }); + + describe('And displayed when `help` command is entered', () => { + it('should display custom help output when Command service has `getHelp()` defined', async () => { + const HelpComponent: React.FunctionComponent = () => { + return
    {'help output'}
    ; + }; + render({ HelpComponent }); + enterCommand('help'); + + await waitFor(() => { + expect(renderResult.getByTestId('custom-help')).toBeTruthy(); + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/management/components/console/components/command_list.tsx b/x-pack/plugins/security_solution/public/management/components/console/components/command_list.tsx index 6fd66bf94c47f..e6323fccb847e 100644 --- a/x-pack/plugins/security_solution/public/management/components/console/components/command_list.tsx +++ b/x-pack/plugins/security_solution/public/management/components/console/components/command_list.tsx @@ -7,7 +7,6 @@ import React, { memo, useMemo, useCallback } from 'react'; import styled from 'styled-components'; -import { groupBy, sortBy } from 'lodash'; import { EuiBadge, EuiBasicTable, @@ -24,6 +23,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { sortBy } from 'lodash'; import type { CommandDefinition } from '../types'; import { useTestIdGenerator } from '../../../hooks/use_test_id_generator'; import { useDataTestSubj } from '../hooks/state_selectors/use_data_test_subj'; @@ -33,6 +33,21 @@ import { getCommandNameWithArgs } from '../service/utils'; import { ConsoleCodeBlock } from './console_code_block'; import { useKibana } from '../../../../common/lib/kibana'; +const otherCommandsGroupLabel = i18n.translate( + 'xpack.securitySolution.console.commandList.otherCommandsGroup.label', + { + defaultMessage: 'Other commands', + } +); + +/** + * Takes a string and removes all non-letters/number from it. + * @param value + */ +export const convertToTestId = (value: string): string => { + return value.replace(/[^A-Za-z0-9]/g, ''); +}; + // @ts-expect-error TS2769 const StyledEuiBasicTable = styled(EuiBasicTable)` margin-top: ${({ theme: { eui } }) => eui.euiSizeS}; @@ -78,12 +93,10 @@ export interface CommandListProps { } export const CommandList = memo(({ commands, display = 'default' }) => { - const getTestId = useTestIdGenerator(useDataTestSubj()); + const getTestId = useTestIdGenerator(useDataTestSubj('commandList')); const dispatch = useConsoleStateDispatch(); const { docLinks } = useKibana().services; - const allowedCommands = commands.filter((command) => command.helpHidden !== true); - const footerMessage = useMemo(() => { return ( (({ commands, display = 'defaul ); }, []); - const otherCommandsGroupLabel = i18n.translate( - 'xpack.securitySolution.console.commandList.otherCommandsGroup.label', - { - defaultMessage: 'Other commands', - } - ); - const updateInputText = useCallback( (text) => () => { dispatch({ @@ -136,23 +142,62 @@ export const CommandList = memo(({ commands, display = 'defaul ); const commandsByGroups = useMemo(() => { - return Object.values(groupBy(allowedCommands, 'helpGroupLabel')).reduce( - (acc, current) => { - if (current[0].helpGroupPosition !== undefined) { - // If it already exists just move it to the end - if (acc[current[0].helpGroupPosition]) { - acc[acc.length] = acc[current[0].helpGroupPosition]; - } - - acc[current[0].helpGroupPosition] = sortBy(current, 'helpCommandPosition'); - } else if (current.length) { - acc.push(sortBy(current, 'helpCommandPosition')); + const helpGroups = new Map< + string, + { label: string; position: number; list: CommandDefinition[] } + >(); + + // We only show commands that are no hidden + const allowedCommands = commands.filter((command) => command.helpHidden !== true); + + for (const allowedCommand of allowedCommands) { + const { helpGroupLabel = otherCommandsGroupLabel, helpGroupPosition = Infinity } = + allowedCommand; + + const groupEntry = helpGroups.get(helpGroupLabel); + + if (groupEntry) { + groupEntry.list.push(allowedCommand); + + // Its possible (but probably not intentionally) that the same Group Label might + // have different positions defined (ex. one has a position, and another does not, + // which defaults to `Infinity`. If we detect that here, then update the group + // position. In the end, the group label will have the last explicitly defined + // position found. + if ( + groupEntry.position === Infinity && + helpGroupPosition !== undefined && + helpGroupPosition !== groupEntry.position + ) { + groupEntry.position = helpGroupPosition; } - return acc; - }, - [] - ); - }, [allowedCommands]); + } else { + helpGroups.set(allowedCommand.helpGroupLabel as string, { + label: helpGroupLabel, + position: helpGroupPosition, + list: [allowedCommand], + }); + } + } + + // Sort by Group position and return an array of arrays with the list of commands per group + return sortBy(Array.from(helpGroups.values()), 'position').map((group) => { + // ensure all commands in this group have a `helpCommandPosition`. Those missing one, will + // be set to `Infinity` so that they are moved to the end. + const groupCommandList = group.list.map((command) => { + if (command.helpCommandPosition === undefined) { + return { + ...command, + helpCommandPosition: Infinity, + }; + } + + return command; + }); + + return sortBy(groupCommandList, 'helpCommandPosition'); + }); + }, [commands]); const getTableItems = useCallback( ( @@ -169,24 +214,35 @@ export const CommandList = memo(({ commands, display = 'defaul [commandsByGroup[0]?.helpGroupLabel ?? otherCommandsGroupLabel]: command, })); }, - [otherCommandsGroupLabel] + [] ); const getTableColumns = useCallback( (commandsByGroup) => { + const groupLabel = commandsByGroup[0]?.helpGroupLabel ?? otherCommandsGroupLabel; + const groupTestIdSuffix = convertToTestId(groupLabel); + return [ { - field: commandsByGroup[0]?.helpGroupLabel ?? otherCommandsGroupLabel, - name: commandsByGroup[0]?.helpGroupLabel ?? otherCommandsGroupLabel, + field: groupLabel, + name:
    {groupLabel}
    , render: (command: CommandDefinition) => { const commandNameWithArgs = getCommandNameWithArgs(command); return ( - + {commandNameWithArgs}
    , + title: ( + + {commandNameWithArgs} + + ), description: ( <> @@ -197,7 +253,6 @@ export const CommandList = memo(({ commands, display = 'defaul ), }, ]} - data-test-subj={getTestId('commandList-command')} /> {command.helpGroupLabel !== HELP_GROUPS.supporting.label && @@ -222,6 +277,9 @@ export const CommandList = memo(({ commands, display = 'defaul aria-label={`updateTextInputCommand-${command.name}`} onClick={updateInputText(`${commandNameWithArgs} `)} isDisabled={command.helpDisabled === true} + data-test-subj={getTestId( + `${groupTestIdSuffix}-${command.name}-addToInput` + )} /> @@ -232,7 +290,7 @@ export const CommandList = memo(({ commands, display = 'defaul }, ]; }, - [getTestId, otherCommandsGroupLabel, updateInputText] + [getTestId, updateInputText] ); const getFilteredCommands = useCallback( @@ -258,7 +316,11 @@ export const CommandList = memo(({ commands, display = 'defaul defaultMessage="{learnMore} about response actions and using the console." values={{ learnMore: ( - + (({ commands, display = 'defaul defaultMessage="Helpful tips:" /> } + data-test-subj={getTestId('helpfulTips')} >
      {calloutItems.map((item, index) => ( @@ -289,21 +352,24 @@ export const CommandList = memo(({ commands, display = 'defaul ); return ( - <> +
      {commandsByGroups.map((commandsByGroup, i) => ( ))} {callout} - +
      ); } return ( - <> +
      {commandsByGroups.map((commandsByGroup) => { const groupLabel = commandsByGroup[0].helpGroupLabel; @@ -345,7 +411,7 @@ export const CommandList = memo(({ commands, display = 'defaul ), }, ]} - data-test-subj={getTestId('commandList-command')} + data-test-subj={getTestId('command')} /> ); @@ -355,7 +421,7 @@ export const CommandList = memo(({ commands, display = 'defaul })} {footerMessage} - +
      ); }); CommandList.displayName = 'CommandList'; diff --git a/x-pack/plugins/security_solution/public/management/components/console/components/console_header.test.tsx b/x-pack/plugins/security_solution/public/management/components/console/components/console_header.test.tsx new file mode 100644 index 0000000000000..4457347832eac --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/console/components/console_header.test.tsx @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import type { ConsoleProps } from '..'; +import type { AppContextTestRender } from '../../../../common/mock/endpoint'; +import { getConsoleTestSetup } from '../mocks'; +import { HELP_LABEL } from './console_header'; + +describe('Console header area', () => { + let render: (props?: Partial) => ReturnType; + let renderResult: ReturnType; + + beforeEach(() => { + const testSetup = getConsoleTestSetup(); + + render = (props = {}) => (renderResult = testSetup.renderConsole(props)); + }); + + it('should display the help button', () => { + render(); + + expect(renderResult.getByTestId('test-header-helpButton').textContent).toEqual(HELP_LABEL); + }); + + it('should not display a title component', () => { + render(); + + expect(renderResult.getByTestId('test-header-titleComponentContainer').textContent).toEqual(''); + }); + + it('should show a title component if one was provided', () => { + render({ TitleComponent: () => <>{'header component here'} }); + + expect(renderResult.getByTestId('test-header-titleComponentContainer').textContent).toEqual( + 'header component here' + ); + }); + + it('should open the side panel when help button is clicked', () => { + render(); + renderResult.getByTestId('test-header-helpButton').click(); + + expect(renderResult.getByTestId('test-sidePanel')).toBeTruthy(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/management/components/console/components/console_header.tsx b/x-pack/plugins/security_solution/public/management/components/console/components/console_header.tsx index 4a88c525d6f2e..7fbc0b53e5413 100644 --- a/x-pack/plugins/security_solution/public/management/components/console/components/console_header.tsx +++ b/x-pack/plugins/security_solution/public/management/components/console/components/console_header.tsx @@ -10,11 +10,18 @@ import styled from 'styled-components'; import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; +import { useDataTestSubj } from '../hooks/state_selectors/use_data_test_subj'; +import { useTestIdGenerator } from '../../../hooks/use_test_id_generator'; import { useConsoleStateDispatch } from '../hooks/state_selectors/use_console_state_dispatch'; import { useWithSidePanel } from '../hooks/state_selectors/use_with_side_panel'; import type { ConsoleProps } from '..'; -const HELP_LABEL = i18n.translate('xpack.securitySolution.console.layoutHeader.helpButtonLabel', { +export const HELP_LABEL = i18n.translate( + 'xpack.securitySolution.console.layoutHeader.helpButtonTitle', + { defaultMessage: 'Help' } +); + +const HELP_TOOLTIP = i18n.translate('xpack.securitySolution.console.layoutHeader.helpButtonLabel', { defaultMessage: 'Show help', }); @@ -28,6 +35,7 @@ export type ConsoleHeaderProps = Pick; export const ConsoleHeader = memo(({ TitleComponent }) => { const dispatch = useConsoleStateDispatch(); const panelCurrentlyShowing = useWithSidePanel().show; + const getTestId = useTestIdGenerator(useDataTestSubj('header')); const isHelpOpen = panelCurrentlyShowing === 'help'; const handleHelpButtonOnClick = useCallback(() => { @@ -44,7 +52,11 @@ export const ConsoleHeader = memo(({ TitleComponent }) => { justifyContent="spaceBetween" responsive={false} > - + {TitleComponent ? : ''} {!isHelpOpen && ( @@ -53,9 +65,10 @@ export const ConsoleHeader = memo(({ TitleComponent }) => { style={{ marginLeft: 'auto' }} onClick={handleHelpButtonOnClick} iconType="help" - title={HELP_LABEL} - aria-label={HELP_LABEL} + title={HELP_TOOLTIP} + aria-label={HELP_TOOLTIP} isSelected={isHelpOpen} + data-test-subj={getTestId('helpButton')} > { render = (props = {}) => (renderResult = testSetup.renderConsole(props)); }); - it('should display all available commands when `help` command is entered', async () => { - render(); - enterCommand('help'); - - expect(renderResult.getByTestId('test-helpOutput')).toBeTruthy(); - - await waitFor(() => { - expect(renderResult.getAllByTestId('test-commandList-command')).toHaveLength(commands.length); - }); - }); - - it('should display custom help output when Command service has `getHelp()` defined', async () => { - const HelpComponent: React.FunctionComponent = () => { - return
      {'help output'}
      ; - }; - render({ HelpComponent }); - enterCommand('help'); - - await waitFor(() => { - expect(renderResult.getByTestId('custom-help')).toBeTruthy(); - }); - }); - it('should clear the command output history when `clear` is entered', async () => { render(); enterCommand('help'); diff --git a/x-pack/plugins/security_solution/public/management/components/console/components/side_panel/side_panel_content_layout.tsx b/x-pack/plugins/security_solution/public/management/components/console/components/side_panel/side_panel_content_layout.tsx index 82b9fdf5fb849..33e8d0acb05a4 100644 --- a/x-pack/plugins/security_solution/public/management/components/console/components/side_panel/side_panel_content_layout.tsx +++ b/x-pack/plugins/security_solution/public/management/components/console/components/side_panel/side_panel_content_layout.tsx @@ -9,6 +9,8 @@ import type { ReactNode } from 'react'; import React, { memo } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule } from '@elastic/eui'; import styled from 'styled-components'; +import { useTestIdGenerator } from '../../../../hooks/use_test_id_generator'; +import { useDataTestSubj } from '../../hooks/state_selectors/use_data_test_subj'; export interface SidePanelContentLayoutProps { children: ReactNode; @@ -24,23 +26,30 @@ const StyledEuiFlexItemNoPadding = styled(EuiFlexItem)` */ export const SidePanelContentLayout = memo( ({ headerContent, children }) => { + const getTestId = useTestIdGenerator(useDataTestSubj('sidePanel')); + return ( {headerContent && ( <> - + {headerContent} )} -
      {children}
      +
      {children}
      ); diff --git a/x-pack/plugins/security_solution/public/management/components/console/components/side_panel/side_panel_content_manager.test.tsx b/x-pack/plugins/security_solution/public/management/components/console/components/side_panel/side_panel_content_manager.test.tsx new file mode 100644 index 0000000000000..119dbbec0cc9a --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/console/components/side_panel/side_panel_content_manager.test.tsx @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ConsoleProps } from '../..'; +import type { AppContextTestRender } from '../../../../../common/mock/endpoint'; +import { getConsoleTestSetup } from '../../mocks'; +import { act } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +describe('When displaying the side panel', () => { + let render: (props?: Partial) => ReturnType; + let renderResult: ReturnType; + + beforeEach(() => { + const testSetup = getConsoleTestSetup(); + + render = (props = {}) => { + renderResult = testSetup.renderConsole(props); + return renderResult; + }; + }); + + describe('and displaying Help content', () => { + let renderAndOpenHelp: typeof render; + + beforeEach(() => { + renderAndOpenHelp = (props) => { + render(props); + act(() => { + userEvent.click(renderResult.getByTestId('test-header-helpButton')); + }); + + expect(renderResult.getByTestId('test-sidePanel')).toBeTruthy(); + + return renderResult; + }; + }); + + it('should display the help panel content', () => { + renderAndOpenHelp(); + + expect(renderResult.getByTestId('test-sidePanel-helpContent')).toBeTruthy(); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/management/components/console/components/side_panel/side_panel_content_manager.tsx b/x-pack/plugins/security_solution/public/management/components/console/components/side_panel/side_panel_content_manager.tsx index a3ccf282898b7..3e0296460aeaf 100644 --- a/x-pack/plugins/security_solution/public/management/components/console/components/side_panel/side_panel_content_manager.tsx +++ b/x-pack/plugins/security_solution/public/management/components/console/components/side_panel/side_panel_content_manager.tsx @@ -19,11 +19,13 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; +import { useTestIdGenerator } from '../../../../hooks/use_test_id_generator'; import { CommandList } from '../command_list'; import { useWithCommandList } from '../../hooks/state_selectors/use_with_command_list'; import { SidePanelContentLayout } from './side_panel_content_layout'; import { useWithSidePanel } from '../../hooks/state_selectors/use_with_side_panel'; import { useConsoleStateDispatch } from '../../hooks/state_selectors/use_console_state_dispatch'; +import { useDataTestSubj } from '../../hooks/state_selectors/use_data_test_subj'; const StyledEuiFlexGroup = styled(EuiFlexGroup)` padding-top: ${({ theme: { eui } }) => eui.euiPanelPaddingModifiers.paddingSmall}; @@ -33,6 +35,7 @@ const StyledEuiFlexGroup = styled(EuiFlexGroup)` export const SidePanelContentManager = memo(() => { const dispatch = useConsoleStateDispatch(); const commands = useWithCommandList(); + const getTestId = useTestIdGenerator(useDataTestSubj('sidePanel')); const show = useWithSidePanel().show; const closeHelpPanel = useCallback(() => { @@ -48,7 +51,7 @@ export const SidePanelContentManager = memo(() => { <> - +

      { iconType="cross" color="text" onClick={closeHelpPanel} + data-test-subj={getTestId('headerCloseButton')} /> @@ -80,15 +84,19 @@ export const SidePanelContentManager = memo(() => { ); } return null; - }, [show, closeHelpPanel]); + }, [show, getTestId, closeHelpPanel]); const panelBody: ReactNode = useMemo(() => { if (show === 'help') { - return ; + return ( +
      + +
      + ); } return null; - }, [commands, show]); + }, [commands, getTestId, show]); if (!show) { return null; diff --git a/x-pack/plugins/security_solution/public/management/components/console/hooks/state_selectors/use_data_test_subj.ts b/x-pack/plugins/security_solution/public/management/components/console/hooks/state_selectors/use_data_test_subj.ts index 144a5a63cd71b..41643708f403a 100644 --- a/x-pack/plugins/security_solution/public/management/components/console/hooks/state_selectors/use_data_test_subj.ts +++ b/x-pack/plugins/security_solution/public/management/components/console/hooks/state_selectors/use_data_test_subj.ts @@ -7,6 +7,16 @@ import { useConsoleStore } from '../../components/console_state/console_state'; -export const useDataTestSubj = (): string | undefined => { - return useConsoleStore().state.dataTestSubj; +/** + * Returns the `data-test-subj` that was defined when the `Console` was rendered. + * Can optionally set a suffix on that value if one is provided + */ +export const useDataTestSubj = (suffix: string = ''): string => { + const dataTestSubj = useConsoleStore().state.dataTestSubj; + + if (!dataTestSubj) { + return ''; + } + + return dataTestSubj + (suffix ? `-${suffix}` : ''); }; diff --git a/x-pack/plugins/security_solution/public/management/components/console/mocks.tsx b/x-pack/plugins/security_solution/public/management/components/console/mocks.tsx index 4dc5d2050450a..2af7963e13959 100644 --- a/x-pack/plugins/security_solution/public/management/components/console/mocks.tsx +++ b/x-pack/plugins/security_solution/public/management/components/console/mocks.tsx @@ -11,6 +11,8 @@ import React, { memo, useEffect } from 'react'; import { EuiCode } from '@elastic/eui'; import userEvent from '@testing-library/user-event'; import { act } from '@testing-library/react'; +import { within } from '@testing-library/dom'; +import { convertToTestId } from './components/command_list'; import { Console } from './console'; import type { CommandArgumentValueSelectorProps, @@ -21,7 +23,19 @@ import type { import type { AppContextTestRender } from '../../../common/mock/endpoint'; import { createAppRootMockRenderer } from '../../../common/mock/endpoint'; -export interface ConsoleTestSetup { +interface ConsoleSelectorsAndActionsMock { + getLeftOfCursorInputText: () => string; + getRightOfCursorInputText: () => string; + getInputText: () => string; + openHelpPanel: () => void; + closeHelpPanel: () => void; +} + +export interface ConsoleTestSetup + extends Pick< + AppContextTestRender, + 'startServices' | 'coreStart' | 'depsStart' | 'queryClient' | 'history' | 'setExperimentalFlag' + > { renderConsole(props?: Partial): ReturnType; commands: CommandDefinition[]; @@ -38,8 +52,54 @@ export interface ConsoleTestSetup { useKeyboard: boolean; }> ): void; + + selectors: ConsoleSelectorsAndActionsMock; } +/** + * A set of jest selectors and actions for interacting with the console + * @param dataTestSubj + */ +export const getConsoleSelectorsAndActionMock = ( + renderResult: ReturnType, + dataTestSubj: string = 'test' +): ConsoleTestSetup['selectors'] => { + const getLeftOfCursorInputText: ConsoleSelectorsAndActionsMock['getLeftOfCursorInputText'] = + () => { + return renderResult.getByTestId(`${dataTestSubj}-cmdInput-leftOfCursor`).textContent ?? ''; + }; + const getRightOfCursorInputText: ConsoleSelectorsAndActionsMock['getRightOfCursorInputText'] = + () => { + return renderResult.getByTestId(`${dataTestSubj}-cmdInput-rightOfCursor`).textContent ?? ''; + }; + const getInputText: ConsoleSelectorsAndActionsMock['getInputText'] = () => { + return getLeftOfCursorInputText() + getRightOfCursorInputText(); + }; + + const isHelpPanelOpen = (): boolean => { + return Boolean(renderResult.queryByTestId(`${dataTestSubj}-sidePanel-helpContent`)); + }; + + const openHelpPanel: ConsoleSelectorsAndActionsMock['openHelpPanel'] = () => { + if (!isHelpPanelOpen()) { + renderResult.getByTestId(`${dataTestSubj}-header-helpButton`).click(); + } + }; + const closeHelpPanel: ConsoleSelectorsAndActionsMock['closeHelpPanel'] = () => { + if (isHelpPanelOpen()) { + renderResult.getByTestId(`${dataTestSubj}-sidePanel-headerCloseButton`).click(); + } + }; + + return { + getInputText, + getLeftOfCursorInputText, + getRightOfCursorInputText, + openHelpPanel, + closeHelpPanel, + }; +}; + /** * Finds the console in the Render Result and enters the command provided * @param renderResult @@ -75,17 +135,23 @@ export const enterConsoleCommand = ( export const getConsoleTestSetup = (): ConsoleTestSetup => { const mockedContext = createAppRootMockRenderer(); + const { startServices, coreStart, depsStart, queryClient, history, setExperimentalFlag } = + mockedContext; let renderResult: ReturnType; const commandList = getCommandListMock(); + let testSubj: string; + const renderConsole: ConsoleTestSetup['renderConsole'] = ({ prompt = '$$>', commands = commandList, 'data-test-subj': dataTestSubj = 'test', ...others } = {}) => { + testSubj = dataTestSubj; + return (renderResult = mockedContext.render( )); @@ -95,10 +161,52 @@ export const getConsoleTestSetup = (): ConsoleTestSetup => { enterConsoleCommand(renderResult, cmd, options); }; + let selectors: ConsoleSelectorsAndActionsMock; + const initSelectorsIfNeeded = () => { + if (selectors) { + return selectors; + } + + if (!testSubj) { + throw new Error(`no 'dataTestSubj' provided to 'render()'!`); + } + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + selectors = getConsoleSelectorsAndActionMock(renderResult, testSubj!); + }; + return { + startServices, + coreStart, + depsStart, + queryClient, + history, + setExperimentalFlag, renderConsole, commands: commandList, enterCommand, + selectors: { + getInputText: () => { + initSelectorsIfNeeded(); + return selectors.getInputText(); + }, + getLeftOfCursorInputText: () => { + initSelectorsIfNeeded(); + return selectors.getLeftOfCursorInputText(); + }, + getRightOfCursorInputText: () => { + initSelectorsIfNeeded(); + return selectors.getRightOfCursorInputText(); + }, + openHelpPanel: () => { + initSelectorsIfNeeded(); + return selectors.openHelpPanel(); + }, + closeHelpPanel: () => { + initSelectorsIfNeeded(); + return selectors.closeHelpPanel(); + }, + }, }; }; @@ -142,11 +250,13 @@ export const getCommandListMock = (): CommandDefinition[] => { name: 'cmd1', about: 'a command with no options', RenderComponent: jest.fn(RenderComponent), + helpGroupLabel: 'group 1', }, { name: 'cmd2', about: 'runs cmd 2', RenderComponent: jest.fn(RenderComponent), + helpGroupLabel: 'group 2', args: { file: { about: 'Includes file in the run', @@ -173,6 +283,7 @@ export const getCommandListMock = (): CommandDefinition[] => { name: 'cmd3', about: 'allows argument to be used multiple times', RenderComponent: jest.fn(RenderComponent), + helpGroupPosition: 0, args: { foo: { about: 'foo stuff', @@ -186,6 +297,7 @@ export const getCommandListMock = (): CommandDefinition[] => { about: 'all options optional, but at least one is required', RenderComponent: jest.fn(RenderComponent), mustHaveArgs: true, + helpGroupPosition: 1, args: { foo: { about: 'foo stuff', @@ -226,6 +338,9 @@ export const getCommandListMock = (): CommandDefinition[] => { mustHaveArgs: true, exampleUsage: 'cmd6 --foo 123', exampleInstruction: 'Enter --foo to execute', + helpGroupLabel: 'group 1', + helpGroupPosition: 0, + helpCommandPosition: 0, args: { foo: { about: 'foo stuff', @@ -245,6 +360,7 @@ export const getCommandListMock = (): CommandDefinition[] => { name: 'cmd7', about: 'Command with argument selector', RenderComponent: jest.fn(RenderComponent), + helpGroupLabel: 'group 2', args: { foo: { about: 'foo stuff', @@ -273,3 +389,46 @@ export const ArgumentSelectorComponentMock = memo< ); }); ArgumentSelectorComponentMock.displayName = 'ArgumentSelectorComponentMock'; + +export interface HelpSidePanelSelectorsAndActions { + getHelpGroupLabels: () => string[]; + getHelpCommandNames: (forGroup?: string) => string[]; +} + +export const getHelpSidePanelSelectorsAndActionsMock = ( + renderResult: ReturnType, + dataTestSubj: string = 'test' +): HelpSidePanelSelectorsAndActions => { + const getHelpGroupLabels: HelpSidePanelSelectorsAndActions['getHelpGroupLabels'] = () => { + // FYI: we're collapsing the labels here because EUI includes mobile elements + // in the DOM that have the same test ids + return Array.from( + new Set( + renderResult + .getAllByTestId(`${dataTestSubj}-commandList-group`) + .map((element) => element.textContent ?? '') + ) + ); + }; + + const getHelpCommandNames: HelpSidePanelSelectorsAndActions['getHelpCommandNames'] = ( + forGroup + ) => { + let searchContainer = renderResult.container; + + if (forGroup) { + searchContainer = renderResult.getByTestId( + `${dataTestSubj}-commandList-${convertToTestId(forGroup)}` + ); + } + + return within(searchContainer) + .getAllByTestId(`${dataTestSubj}-commandList-commandName`) + .map((commandEle) => commandEle.textContent ?? ''); + }; + + return { + getHelpGroupLabels, + getHelpCommandNames, + }; +}; diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/lib/console_commands_definition.ts b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/lib/console_commands_definition.ts index 4c603888bdcc5..02ce95fbc25be 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/lib/console_commands_definition.ts +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/lib/console_commands_definition.ts @@ -118,7 +118,7 @@ const capabilitiesAndPrivilegesValidator = (command: Command): true | string => return true; }; -const HELP_GROUPS = Object.freeze({ +export const HELP_GROUPS = Object.freeze({ responseActions: { position: 0, label: i18n.translate('xpack.securitySolution.endpointConsoleCommands.groups.responseActions', { diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/lib/integration_tests/console_commands_definition.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/lib/integration_tests/console_commands_definition.test.tsx new file mode 100644 index 0000000000000..f5e96f0e8a706 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/lib/integration_tests/console_commands_definition.test.tsx @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ConsoleTestSetup, HelpSidePanelSelectorsAndActions } from '../../../console/mocks'; +import { + getConsoleTestSetup, + getHelpSidePanelSelectorsAndActionsMock, +} from '../../../console/mocks'; +import { getEndpointConsoleCommands } from '../..'; +import { EndpointMetadataGenerator } from '../../../../../../common/endpoint/data_generators/endpoint_metadata_generator'; +import { getEndpointPrivilegesInitialStateMock } from '../../../../../common/components/user_privileges/endpoint/mocks'; +import { sortBy } from 'lodash'; +import { HELP_GROUPS } from '../console_commands_definition'; + +describe('When displaying Endpoint Response Actions', () => { + let render: ConsoleTestSetup['renderConsole']; + let renderResult: ReturnType; + let consoleSelectors: ConsoleTestSetup['selectors']; + let helpPanelSelectors: HelpSidePanelSelectorsAndActions; + + beforeEach(() => { + const testSetup = getConsoleTestSetup(); + + testSetup.setExperimentalFlag({ + responseActionGetFileEnabled: true, + responseActionExecuteEnabled: true, + }); + + const endpointMetadata = new EndpointMetadataGenerator().generate(); + const commands = getEndpointConsoleCommands({ + endpointAgentId: '123', + endpointCapabilities: endpointMetadata.Endpoint.capabilities ?? [], + endpointPrivileges: getEndpointPrivilegesInitialStateMock(), + }); + + consoleSelectors = testSetup.selectors; + render = (props = { commands }) => { + renderResult = testSetup.renderConsole(props); + helpPanelSelectors = getHelpSidePanelSelectorsAndActionsMock(renderResult); + + return renderResult; + }; + }); + + it('should display expected help groups', () => { + render(); + consoleSelectors.openHelpPanel(); + + expect(helpPanelSelectors.getHelpGroupLabels()).toEqual([ + ...sortBy(Object.values(HELP_GROUPS), 'position').map((group) => group.label), + 'Supporting commands & parameters', + ]); + }); + + it('should display response action commands in the help panel in expected order', () => { + render(); + consoleSelectors.openHelpPanel(); + const commands = helpPanelSelectors.getHelpCommandNames(HELP_GROUPS.responseActions.label); + + expect(commands).toEqual([ + 'isolate', + 'release', + 'status', + 'processes', + 'kill-process --pid', + 'suspend-process --pid', + 'get-file --path', + 'execute --command', + ]); + }); +}); diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/endpoint/endpoints.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/endpoint/endpoints.cy.ts index 3eec34c967c21..982e4857086ba 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/endpoint/endpoints.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/endpoint/endpoints.cy.ts @@ -5,15 +5,91 @@ * 2.0. */ +import type { Agent } from '@kbn/fleet-plugin/common'; +import { ENDPOINT_VM_NAME } from '../../tasks/common'; +import { + getAgentByHostName, + getEndpointIntegrationVersion, + reassignAgentPolicy, +} from '../../tasks/fleet'; +import type { IndexedFleetEndpointPolicyResponse } from '../../../../../common/endpoint/data_loaders/index_fleet_endpoint_policy'; +import { getEndpointListPath } from '../../../common/routing'; import { login } from '../../tasks/login'; +import { + AGENT_HOSTNAME_CELL, + TABLE_ROW_ACTIONS, + TABLE_ROW_ACTIONS_MENU, + AGENT_POLICY_CELL, +} from '../../screens/endpoints'; +import { + FLEET_REASSIGN_POLICY_MODAL, + FLEET_REASSIGN_POLICY_MODAL_CONFIRM_BUTTON, +} from '../../screens/fleet'; describe('Endpoints page', () => { + const endpointHostname = Cypress.env(ENDPOINT_VM_NAME); + beforeEach(() => { login(); }); - it('Loads the endpoints page', () => { - cy.visit('/app/security/administration/endpoints'); + it('Shows endpoint on the list', () => { + cy.visit(getEndpointListPath({ name: 'endpointList' })); cy.contains('Hosts running Elastic Defend').should('exist'); + cy.getByTestSubj(AGENT_HOSTNAME_CELL).should('have.text', endpointHostname); + }); + + describe('Endpoint reassignment', () => { + let response: IndexedFleetEndpointPolicyResponse; + let initialAgentData: Agent; + + before(() => { + getAgentByHostName(endpointHostname).then((agentData) => { + initialAgentData = agentData; + }); + getEndpointIntegrationVersion().then((version) => { + cy.task('indexFleetEndpointPolicy', { + policyName: `Reassign ${Math.random().toString(36).substr(2, 5)}`, + endpointPackageVersion: version, + }).then((data) => { + response = data; + }); + }); + }); + + beforeEach(() => { + login(); + }); + + after(() => { + if (initialAgentData?.policy_id) { + reassignAgentPolicy(initialAgentData.id, initialAgentData.policy_id); + } + if (response) { + cy.task('deleteIndexedFleetEndpointPolicies', response); + } + }); + + it('User can reassign a single endpoint to a different Agent Configuration', () => { + cy.visit(getEndpointListPath({ name: 'endpointList' })); + const hostname = cy + .getByTestSubj(AGENT_HOSTNAME_CELL) + .filter(`:contains("${endpointHostname}")`); + const tableRow = hostname.parents('tr'); + tableRow.getByTestSubj(TABLE_ROW_ACTIONS).click(); + cy.getByTestSubj(TABLE_ROW_ACTIONS_MENU).contains('Reassign agent policy').click(); + cy.getByTestSubj(FLEET_REASSIGN_POLICY_MODAL) + .find('select') + .select(response.agentPolicies[0].name); + cy.getByTestSubj(FLEET_REASSIGN_POLICY_MODAL_CONFIRM_BUTTON).click(); + cy.getByTestSubj(AGENT_HOSTNAME_CELL) + .filter(`:contains("${endpointHostname}")`) + .should('exist'); + cy.getByTestSubj(AGENT_HOSTNAME_CELL) + .filter(`:contains("${endpointHostname}")`) + .parents('tr') + .getByTestSubj(AGENT_POLICY_CELL) + .should('have.text', response.agentPolicies[0].name); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/artifact_tabs_in_policy_details.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/artifact_tabs_in_policy_details.cy.ts index 63ca9bc8b5c97..bdf548a833ac6 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/artifact_tabs_in_policy_details.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/artifact_tabs_in_policy_details.cy.ts @@ -64,8 +64,8 @@ const visitArtifactTab = (tabId: string) => { const visitPolicyDetailsPage = () => { cy.visit('/app/security/administration/policy'); - cy.getBySel('policyNameCellLink').eq(0).click({ force: true }); - cy.getBySel('policyDetailsPage').should('exist'); + cy.getByTestSubj('policyNameCellLink').eq(0).click({ force: true }); + cy.getByTestSubj('policyDetailsPage').should('exist'); cy.get('#settings').should('exist'); // waiting for Policy Settings tab }; @@ -99,18 +99,18 @@ describe('Artifact tabs in Policy Details page', () => { loginWithPrivilegeRead(testData.privilegePrefix); visitArtifactTab(testData.tabId); - cy.getBySel('policy-artifacts-empty-unexisting').should('exist'); + cy.getByTestSubj('policy-artifacts-empty-unexisting').should('exist'); - cy.getBySel('unexisting-manage-artifacts-button').should('not.exist'); + cy.getByTestSubj('unexisting-manage-artifacts-button').should('not.exist'); }); it(`[ALL] User can add ${testData.title} artifact`, () => { loginWithPrivilegeAll(); visitArtifactTab(testData.tabId); - cy.getBySel('policy-artifacts-empty-unexisting').should('exist'); + cy.getByTestSubj('policy-artifacts-empty-unexisting').should('exist'); - cy.getBySel('unexisting-manage-artifacts-button').should('exist').click(); + cy.getByTestSubj('unexisting-manage-artifacts-button').should('exist').click(); const { formActions, checkResults } = testData.create; @@ -118,18 +118,18 @@ describe('Artifact tabs in Policy Details page', () => { // Add a per policy artifact - but not assign it to any policy cy.get('[data-test-subj$="-perPolicy"]').click(); // test-subjects are generated in different formats, but all ends with -perPolicy - cy.getBySel(`${testData.pagePrefix}-flyout-submitButton`).click(); + cy.getByTestSubj(`${testData.pagePrefix}-flyout-submitButton`).click(); // Check new artifact is in the list for (const checkResult of checkResults) { - cy.getBySel(checkResult.selector).should('have.text', checkResult.value); + cy.getByTestSubj(checkResult.selector).should('have.text', checkResult.value); } - cy.getBySel('policyDetailsPage').should('not.exist'); - cy.getBySel('backToOrigin').contains(/^Back to .+ policy$/); + cy.getByTestSubj('policyDetailsPage').should('not.exist'); + cy.getByTestSubj('backToOrigin').contains(/^Back to .+ policy$/); - cy.getBySel('backToOrigin').click(); - cy.getBySel('policyDetailsPage').should('exist'); + cy.getByTestSubj('backToOrigin').click(); + cy.getByTestSubj('policyDetailsPage').should('exist'); }); }); @@ -144,34 +144,34 @@ describe('Artifact tabs in Policy Details page', () => { loginWithPrivilegeRead(testData.privilegePrefix); visitArtifactTab(testData.tabId); - cy.getBySel('policy-artifacts-empty-unassigned').should('exist'); + cy.getByTestSubj('policy-artifacts-empty-unassigned').should('exist'); - cy.getBySel('unassigned-manage-artifacts-button').should('not.exist'); - cy.getBySel('unassigned-assign-artifacts-button').should('not.exist'); + cy.getByTestSubj('unassigned-manage-artifacts-button').should('not.exist'); + cy.getByTestSubj('unassigned-assign-artifacts-button').should('not.exist'); }); it(`[ALL] User can Manage and Assign ${testData.title} artifacts`, () => { loginWithPrivilegeAll(); visitArtifactTab(testData.tabId); - cy.getBySel('policy-artifacts-empty-unassigned').should('exist'); + cy.getByTestSubj('policy-artifacts-empty-unassigned').should('exist'); // Manage artifacts - cy.getBySel('unassigned-manage-artifacts-button').should('exist').click(); + cy.getByTestSubj('unassigned-manage-artifacts-button').should('exist').click(); cy.location('pathname').should( 'equal', `/app/security/administration/${testData.urlPath}` ); - cy.getBySel('backToOrigin').click(); + cy.getByTestSubj('backToOrigin').click(); // Assign artifacts - cy.getBySel('unassigned-assign-artifacts-button').should('exist').click(); + cy.getByTestSubj('unassigned-assign-artifacts-button').should('exist').click(); - cy.getBySel('artifacts-assign-flyout').should('exist'); - cy.getBySel('artifacts-assign-confirm-button').should('be.disabled'); + cy.getByTestSubj('artifacts-assign-flyout').should('exist'); + cy.getByTestSubj('artifacts-assign-confirm-button').should('be.disabled'); - cy.getBySel(`${testData.artifactName}_checkbox`).click(); - cy.getBySel('artifacts-assign-confirm-button').click(); + cy.getByTestSubj(`${testData.artifactName}_checkbox`).click(); + cy.getByTestSubj('artifacts-assign-confirm-button').click(); }); }); @@ -189,17 +189,17 @@ describe('Artifact tabs in Policy Details page', () => { visitArtifactTab(testData.tabId); // List of artifacts - cy.getBySel('artifacts-collapsed-list-card').should('have.length', 1); - cy.getBySel('artifacts-collapsed-list-card-header-titleHolder').contains( + cy.getByTestSubj('artifacts-collapsed-list-card').should('have.length', 1); + cy.getByTestSubj('artifacts-collapsed-list-card-header-titleHolder').contains( testData.artifactName ); // Cannot assign artifacts - cy.getBySel('artifacts-assign-button').should('not.exist'); + cy.getByTestSubj('artifacts-assign-button').should('not.exist'); // Cannot remove from policy - cy.getBySel('artifacts-collapsed-list-card-header-actions-button').click(); - cy.getBySel('remove-from-policy-action').should('not.exist'); + cy.getByTestSubj('artifacts-collapsed-list-card-header-actions-button').click(); + cy.getByTestSubj('remove-from-policy-action').should('not.exist'); }); it(`[ALL] User can see ${testData.title} artifacts and can assign or remove artifacts from policy`, () => { @@ -207,20 +207,20 @@ describe('Artifact tabs in Policy Details page', () => { visitArtifactTab(testData.tabId); // List of artifacts - cy.getBySel('artifacts-collapsed-list-card').should('have.length', 1); - cy.getBySel('artifacts-collapsed-list-card-header-titleHolder').contains( + cy.getByTestSubj('artifacts-collapsed-list-card').should('have.length', 1); + cy.getByTestSubj('artifacts-collapsed-list-card-header-titleHolder').contains( testData.artifactName ); // Assign artifacts - cy.getBySel('artifacts-assign-button').should('exist').click(); - cy.getBySel('artifacts-assign-flyout').should('exist'); - cy.getBySel('artifacts-assign-cancel-button').click(); + cy.getByTestSubj('artifacts-assign-button').should('exist').click(); + cy.getByTestSubj('artifacts-assign-flyout').should('exist'); + cy.getByTestSubj('artifacts-assign-cancel-button').click(); // Remove from policy - cy.getBySel('artifacts-collapsed-list-card-header-actions-button').click(); - cy.getBySel('remove-from-policy-action').click(); - cy.getBySel('confirmModalConfirmButton').click(); + cy.getByTestSubj('artifacts-collapsed-list-card-header-actions-button').click(); + cy.getByTestSubj('remove-from-policy-action').click(); + cy.getByTestSubj('confirmModalConfirmButton').click(); cy.contains('Successfully removed'); }); diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/artifacts.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/artifacts.cy.ts index b1899da0413ed..0e468af132aae 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/artifacts.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/artifacts.cy.ts @@ -19,7 +19,7 @@ import { performUserActions } from '../../tasks/perform_user_actions'; import { loadEndpointDataForEventFiltersIfNeeded } from '../../tasks/load_endpoint_data'; const loginWithWriteAccess = (url: string) => { - loginWithRole(ROLE.analyst_hunter); + loginWithRole(ROLE.endpoint_security_policy_manager); cy.visit(url); }; @@ -51,10 +51,10 @@ describe('Artifacts pages', () => { describe(`When on the ${testData.title} entries list`, () => { it(`no access - should show no privileges callout`, () => { loginWithoutAccess(`/app/security/administration/${testData.urlPath}`); - cy.getBySel('noPrivilegesPage').should('exist'); - cy.getBySel('empty-page-feature-action').should('exist'); - cy.getBySel(testData.emptyState).should('not.exist'); - cy.getBySel(`${testData.pagePrefix}-emptyState-addButton`).should('not.exist'); + cy.getByTestSubj('noPrivilegesPage').should('exist'); + cy.getByTestSubj('empty-page-feature-action').should('exist'); + cy.getByTestSubj(testData.emptyState).should('not.exist'); + cy.getByTestSubj(`${testData.pagePrefix}-emptyState-addButton`).should('not.exist'); }); it(`read - should show empty state page if there is no ${testData.title} entry and the add button does not exist`, () => { @@ -62,33 +62,33 @@ describe('Artifacts pages', () => { testData.privilegePrefix, `/app/security/administration/${testData.urlPath}` ); - cy.getBySel(testData.emptyState).should('exist'); - cy.getBySel(`${testData.pagePrefix}-emptyState-addButton`).should('not.exist'); + cy.getByTestSubj(testData.emptyState).should('exist'); + cy.getByTestSubj(`${testData.pagePrefix}-emptyState-addButton`).should('not.exist'); }); it(`write - should show empty state page if there is no ${testData.title} entry and the add button exists`, () => { loginWithWriteAccess(`/app/security/administration/${testData.urlPath}`); - cy.getBySel(testData.emptyState).should('exist'); - cy.getBySel(`${testData.pagePrefix}-emptyState-addButton`).should('exist'); + cy.getByTestSubj(testData.emptyState).should('exist'); + cy.getByTestSubj(`${testData.pagePrefix}-emptyState-addButton`).should('exist'); }); it(`write - should create new ${testData.title} entry`, () => { loginWithWriteAccess(`/app/security/administration/${testData.urlPath}`); // Opens add flyout - cy.getBySel(`${testData.pagePrefix}-emptyState-addButton`).click(); + cy.getByTestSubj(`${testData.pagePrefix}-emptyState-addButton`).click(); performUserActions(testData.create.formActions); // Submit create artifact form - cy.getBySel(`${testData.pagePrefix}-flyout-submitButton`).click(); + cy.getByTestSubj(`${testData.pagePrefix}-flyout-submitButton`).click(); // Check new artifact is in the list for (const checkResult of testData.create.checkResults) { - cy.getBySel(checkResult.selector).should('have.text', checkResult.value); + cy.getByTestSubj(checkResult.selector).should('have.text', checkResult.value); } // Title is shown after adding an item - cy.getBySel('header-page-title').contains(testData.title); + cy.getByTestSubj('header-page-title').contains(testData.title); }); it(`read - should not be able to update/delete an existing ${testData.title} entry`, () => { @@ -96,10 +96,10 @@ describe('Artifacts pages', () => { testData.privilegePrefix, `/app/security/administration/${testData.urlPath}` ); - cy.getBySel('header-page-title').contains(testData.title); - cy.getBySel(`${testData.pagePrefix}-card-header-actions-button`).should('not.exist'); - cy.getBySel(`${testData.pagePrefix}-card-cardEditAction`).should('not.exist'); - cy.getBySel(`${testData.pagePrefix}-card-cardDeleteAction`).should('not.exist'); + cy.getByTestSubj('header-page-title').contains(testData.title); + cy.getByTestSubj(`${testData.pagePrefix}-card-header-actions-button`).should('not.exist'); + cy.getByTestSubj(`${testData.pagePrefix}-card-cardEditAction`).should('not.exist'); + cy.getByTestSubj(`${testData.pagePrefix}-card-cardDeleteAction`).should('not.exist'); }); it(`read - should not be able to create a new ${testData.title} entry`, () => { @@ -107,39 +107,39 @@ describe('Artifacts pages', () => { testData.privilegePrefix, `/app/security/administration/${testData.urlPath}` ); - cy.getBySel('header-page-title').contains(testData.title); - cy.getBySel(`${testData.pagePrefix}-pageAddButton`).should('not.exist'); + cy.getByTestSubj('header-page-title').contains(testData.title); + cy.getByTestSubj(`${testData.pagePrefix}-pageAddButton`).should('not.exist'); }); it(`write - should be able to update an existing ${testData.title} entry`, () => { loginWithWriteAccess(`/app/security/administration/${testData.urlPath}`); // Opens edit flyout - cy.getBySel(`${testData.pagePrefix}-card-header-actions-button`).click(); - cy.getBySel(`${testData.pagePrefix}-card-cardEditAction`).click(); + cy.getByTestSubj(`${testData.pagePrefix}-card-header-actions-button`).click(); + cy.getByTestSubj(`${testData.pagePrefix}-card-cardEditAction`).click(); performUserActions(testData.update.formActions); // Submit edit artifact form - cy.getBySel(`${testData.pagePrefix}-flyout-submitButton`).click(); + cy.getByTestSubj(`${testData.pagePrefix}-flyout-submitButton`).click(); for (const checkResult of testData.create.checkResults) { - cy.getBySel(checkResult.selector).should('have.text', checkResult.value); + cy.getByTestSubj(checkResult.selector).should('have.text', checkResult.value); } // Title still shown after editing an item - cy.getBySel('header-page-title').contains(testData.title); + cy.getByTestSubj('header-page-title').contains(testData.title); }); it(`write - should be able to delete the existing ${testData.title} entry`, () => { loginWithWriteAccess(`/app/security/administration/${testData.urlPath}`); // Remove it - cy.getBySel(`${testData.pagePrefix}-card-header-actions-button`).click(); - cy.getBySel(`${testData.pagePrefix}-card-cardDeleteAction`).click(); - cy.getBySel(`${testData.pagePrefix}-deleteModal-submitButton`).click(); + cy.getByTestSubj(`${testData.pagePrefix}-card-header-actions-button`).click(); + cy.getByTestSubj(`${testData.pagePrefix}-card-cardDeleteAction`).click(); + cy.getByTestSubj(`${testData.pagePrefix}-deleteModal-submitButton`).click(); // No card visible after removing it - cy.getBySel(testData.delete.card).should('not.exist'); + cy.getByTestSubj(testData.delete.card).should('not.exist'); // Empty state is displayed after removing last item - cy.getBySel(testData.emptyState).should('exist'); + cy.getByTestSubj(testData.emptyState).should('exist'); }); }); } diff --git a/x-pack/plugins/security_solution/public/management/cypress/screens/endpoints.ts b/x-pack/plugins/security_solution/public/management/cypress/screens/endpoints.ts new file mode 100644 index 0000000000000..199659142adc0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/cypress/screens/endpoints.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 const AGENT_HOSTNAME_CELL = 'hostnameCellLink'; +export const AGENT_POLICY_CELL = 'policyNameCellLink'; +export const TABLE_ROW_ACTIONS = 'endpointTableRowActions'; +export const TABLE_ROW_ACTIONS_MENU = 'tableRowActionsMenuPanel'; diff --git a/x-pack/plugins/rule_registry/common/field_map/types.ts b/x-pack/plugins/security_solution/public/management/cypress/screens/fleet.ts similarity index 56% rename from x-pack/plugins/rule_registry/common/field_map/types.ts rename to x-pack/plugins/security_solution/public/management/cypress/screens/fleet.ts index 52ee246375ad0..b68cac5c6d495 100644 --- a/x-pack/plugins/rule_registry/common/field_map/types.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/screens/fleet.ts @@ -5,13 +5,5 @@ * 2.0. */ -export interface FieldMap { - [key: string]: { - type: string; - required?: boolean; - array?: boolean; - path?: string; - scaling_factor?: number; - dynamic?: 'strict' | boolean; - }; -} +export const FLEET_REASSIGN_POLICY_MODAL = 'agentReassignPolicyModal'; +export const FLEET_REASSIGN_POLICY_MODAL_CONFIRM_BUTTON = 'confirmModalConfirmButton'; diff --git a/x-pack/plugins/security_solution/public/management/cypress/support/data_loaders.ts b/x-pack/plugins/security_solution/public/management/cypress/support/data_loaders.ts new file mode 100644 index 0000000000000..cab36b7c4d175 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/cypress/support/data_loaders.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +// / + +import { createKbnClient } from '../../../../scripts/endpoint/common/stack_services'; +import type { IndexedFleetEndpointPolicyResponse } from '../../../../common/endpoint/data_loaders/index_fleet_endpoint_policy'; +import { + indexFleetEndpointPolicy, + deleteIndexedFleetEndpointPolicies, +} from '../../../../common/endpoint/data_loaders/index_fleet_endpoint_policy'; + +export const dataLoaders = (on: Cypress.PluginEvents, config: Cypress.PluginConfigOptions) => { + const kbnClient = createKbnClient({ + url: config.env.KIBANA_URL, + username: config.env.ELASTICSEARCH_USERNAME, + password: config.env.ELASTICSEARCH_PASSWORD, + }); + + on('task', { + indexFleetEndpointPolicy: async ({ + policyName, + endpointPackageVersion, + }: { + policyName: string; + endpointPackageVersion: string; + }) => { + return indexFleetEndpointPolicy(kbnClient, policyName, endpointPackageVersion); + }, + deleteIndexedFleetEndpointPolicies: async (indexData: IndexedFleetEndpointPolicyResponse) => { + return deleteIndexedFleetEndpointPolicies(kbnClient, indexData); + }, + }); +}; diff --git a/x-pack/plugins/security_solution/public/management/cypress/support/e2e.ts b/x-pack/plugins/security_solution/public/management/cypress/support/e2e.ts index 1833e52787e18..a5bcaf43e9b6b 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/support/e2e.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/support/e2e.ts @@ -31,13 +31,17 @@ declare global { // eslint-disable-next-line @typescript-eslint/no-namespace namespace Cypress { interface Chainable { - getBySel(...args: Parameters): Chainable>; + getByTestSubj(...args: Parameters): Chainable>; } } } -Cypress.Commands.add('getBySel', (selector, ...args) => - cy.get(`[data-test-subj="${selector}"]`, ...args) -); +Cypress.Commands.addQuery('getByTestSubj', function getByTestSubj(selector, options) { + const getFn = cy.now('get', `[data-test-subj="${selector}"]`, options) as ( + subject: Cypress.Chainable> + ) => Cypress.Chainable>; + + return (subject) => getFn(subject); +}); Cypress.on('uncaught:exception', () => false); diff --git a/x-pack/plugins/security_solution/public/management/cypress/tasks/artifacts.ts b/x-pack/plugins/security_solution/public/management/cypress/tasks/artifacts.ts index 53b191fbe3cfb..8a120b090c33f 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/tasks/artifacts.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/tasks/artifacts.ts @@ -6,6 +6,10 @@ */ import { PACKAGE_POLICY_API_ROOT } from '@kbn/fleet-plugin/common'; +import type { + ExceptionListItemSchema, + ExceptionListSchema, +} from '@kbn/securitysolution-io-ts-list-types'; import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; import { ENDPOINT_ARTIFACT_LISTS, @@ -13,8 +17,7 @@ import { EXCEPTION_LIST_ITEM_URL, EXCEPTION_LIST_URL, } from '@kbn/securitysolution-list-constants'; - -const API_HEADER = { 'kbn-xsrf': 'kibana' }; +import { request } from './common'; export const removeAllArtifacts = () => { for (const listId of ENDPOINT_ARTIFACT_LIST_IDS) { @@ -23,10 +26,9 @@ export const removeAllArtifacts = () => { }; export const removeExceptionsList = (listId: string) => { - cy.request({ + request({ method: 'DELETE', url: `${EXCEPTION_LIST_URL}?list_id=${listId}&namespace_type=agnostic`, - headers: API_HEADER, failOnStatusCode: false, }).then(({ status }) => { expect(status).to.be.oneOf([200, 404]); // should either be success or not found @@ -42,10 +44,9 @@ const ENDPOINT_ARTIFACT_LIST_TYPES = { }; export const createArtifactList = (listId: string) => { - cy.request({ + request({ method: 'POST', url: EXCEPTION_LIST_URL, - headers: API_HEADER, body: { name: listId, description: 'This is a test list', @@ -61,11 +62,9 @@ export const createArtifactList = (listId: string) => { }; export const createPerPolicyArtifact = (name: string, body: object, policyId?: 'all' | string) => { - cy.request({ + request({ method: 'POST', url: EXCEPTION_LIST_ITEM_URL, - - headers: API_HEADER, body: { name, description: '', diff --git a/x-pack/plugins/security_solution/public/management/cypress/tasks/common.ts b/x-pack/plugins/security_solution/public/management/cypress/tasks/common.ts new file mode 100644 index 0000000000000..8d8f797a99883 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/cypress/tasks/common.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. + */ + +export const ENDPOINT_VM_NAME = 'ENDPOINT_VM_NAME'; + +export const API_AUTH = { + user: Cypress.env('ELASTICSEARCH_USERNAME'), + pass: Cypress.env('ELASTICSEARCH_PASSWORD'), +}; + +export const API_HEADERS = { 'kbn-xsrf': 'cypress' }; + +export const request = ( + options: Partial +): Cypress.Chainable> => + cy.request({ + auth: API_AUTH, + headers: API_HEADERS, + ...options, + }); diff --git a/x-pack/plugins/security_solution/public/management/cypress/tasks/fleet.ts b/x-pack/plugins/security_solution/public/management/cypress/tasks/fleet.ts new file mode 100644 index 0000000000000..14a93368414a0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/cypress/tasks/fleet.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Agent, GetAgentsResponse, GetInfoResponse } from '@kbn/fleet-plugin/common'; +import { agentRouteService, epmRouteService } from '@kbn/fleet-plugin/common'; +import type { PutAgentReassignResponse } from '@kbn/fleet-plugin/common/types'; +import { request } from './common'; + +export const getEndpointIntegrationVersion = (): Cypress.Chainable => + request({ + url: epmRouteService.getInfoPath('endpoint'), + method: 'GET', + }).then((response) => response.body.item.version); + +export const getAgentByHostName = (hostname: string): Cypress.Chainable => + request({ + url: agentRouteService.getListPath(), + method: 'GET', + qs: { + kuery: `local_metadata.host.hostname: "${hostname}"`, + }, + }).then((response) => response.body.items[0]); + +export const reassignAgentPolicy = ( + agentId: string, + agentPolicyId: string +): Cypress.Chainable> => + request({ + url: agentRouteService.getReassignPath(agentId), + method: 'PUT', + body: { + policy_id: agentPolicyId, + }, + }); diff --git a/x-pack/plugins/security_solution/public/management/cypress/tasks/load_endpoint_data.ts b/x-pack/plugins/security_solution/public/management/cypress/tasks/load_endpoint_data.ts index 5e6650404e29a..b8029a8528e94 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/tasks/load_endpoint_data.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/tasks/load_endpoint_data.ts @@ -8,17 +8,17 @@ import { isEmpty } from 'lodash'; import { BASE_ENDPOINT_ROUTE } from '../../../../common/endpoint/constants'; import { runEndpointLoaderScript } from './run_endpoint_loader'; +import { request } from './common'; // Checks for Endpoint data and creates it if needed export const loadEndpointDataForEventFiltersIfNeeded = () => { - cy.request({ + request({ method: 'POST', url: `${BASE_ENDPOINT_ROUTE}/suggestions/eventFilters`, body: { field: 'agent.type', query: '', }, - headers: { 'kbn-xsrf': 'kibana' }, failOnStatusCode: false, }).then(({ body }) => { if (isEmpty(body)) { diff --git a/x-pack/plugins/security_solution/public/management/cypress/tasks/login.ts b/x-pack/plugins/security_solution/public/management/cypress/tasks/login.ts index c213693f37f5b..3ac1cfb1b02e4 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/tasks/login.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/tasks/login.ts @@ -11,10 +11,11 @@ import * as yaml from 'js-yaml'; import type { UrlObject } from 'url'; import Url from 'url'; import type { Role } from '@kbn/security-plugin/common'; +import { request } from './common'; import { getT1Analyst } from '../../../../scripts/endpoint/common/roles_users/t1_analyst'; import { getT2Analyst } from '../../../../scripts/endpoint/common/roles_users/t2_analyst'; import { getHunter } from '../../../../scripts/endpoint/common/roles_users/hunter'; -import { getThreadIntelligenceAnalyst } from '../../../../scripts/endpoint/common/roles_users/thread_intelligence_analyst'; +import { getThreatIntelligenceAnalyst } from '../../../../scripts/endpoint/common/roles_users/threat_intelligence_analyst'; import { getSocManager } from '../../../../scripts/endpoint/common/roles_users/soc_manager'; import { getPlatformEngineer } from '../../../../scripts/endpoint/common/roles_users/platform_engineer'; import { getEndpointOperationsAnalyst } from '../../../../scripts/endpoint/common/roles_users/endpoint_operations_analyst'; @@ -25,7 +26,7 @@ export enum ROLE { t1_analyst = 't1Analyst', t2_analyst = 't2Analyst', analyst_hunter = 'hunter', - thread_intelligence_analyst = 'threadIntelligenceAnalyst', + threat_intelligence_analyst = 'threatIntelligenceAnalyst', detections_engineer = 'detectionsEngineer', soc_manager = 'socManager', platform_engineer = 'platformEngineer', @@ -37,7 +38,7 @@ export const rolesMapping: { [key in ROLE]: Omit } = { t1Analyst: getT1Analyst(), t2Analyst: getT2Analyst(), hunter: getHunter(), - threadIntelligenceAnalyst: getThreadIntelligenceAnalyst(), + threatIntelligenceAnalyst: getThreatIntelligenceAnalyst(), socManager: getSocManager(), platformEngineer: getPlatformEngineer(), endpointOperationsAnalyst: getEndpointOperationsAnalyst(), @@ -79,13 +80,6 @@ const ELASTICSEARCH_PASSWORD = 'ELASTICSEARCH_PASSWORD'; */ const LOGIN_API_ENDPOINT = '/internal/security/login'; -const API_AUTH = { - user: Cypress.env(ELASTICSEARCH_USERNAME), - pass: Cypress.env(ELASTICSEARCH_PASSWORD), -}; - -const API_HEADERS = { 'kbn-xsrf': 'cypress' }; - /** * cy.visit will default to the baseUrl which uses the default kibana test user * This function will override that functionality in cy.visit by building the baseUrl @@ -151,52 +145,40 @@ export const createRoleAndUser = (role: ROLE) => { }; export const createCustomRoleAndUser = (role: string, rolePrivileges: Omit) => { - const env = getCurlScriptEnvVars(); - // post the role - cy.request({ + request({ method: 'PUT', - url: `${env.KIBANA_URL}/api/security/role/${role}`, + url: `/api/security/role/${role}`, body: rolePrivileges, - headers: API_HEADERS, - auth: API_AUTH, }); // post the user associated with the role to elasticsearch - cy.request({ + request({ method: 'POST', - url: `${env.KIBANA_URL}/internal/security/users/${role}`, - headers: API_HEADERS, + url: `/internal/security/users/${role}`, body: { username: role, password: Cypress.env(ELASTICSEARCH_PASSWORD), roles: [role], }, - auth: API_AUTH, }); }; export const deleteRoleAndUser = (role: ROLE) => { - const env = getCurlScriptEnvVars(); - - cy.request({ + request({ method: 'DELETE', - auth: API_AUTH, - headers: API_HEADERS, - url: `${env.KIBANA_URL}/internal/security/users/${role}`, + url: `/internal/security/users/${role}`, }); - cy.request({ + request({ method: 'DELETE', - auth: API_AUTH, - headers: API_HEADERS, - url: `${env.KIBANA_URL}/api/security/role/${role}`, + url: `/api/security/role/${role}`, }); }; export const loginWithUser = (user: User) => { const url = Cypress.config().baseUrl; - cy.request({ + request({ body: { providerType: 'basic', providerName: url && !url.includes('localhost') ? 'cloud-basic' : 'basic', @@ -227,7 +209,7 @@ export const loginWithCustomRole = async (role: string, rolePrivileges: Omit { ); // programmatically authenticate without interacting with the Kibana login page - cy.request({ + request({ body: { providerType: 'basic', providerName: url && !url.includes('localhost') ? 'cloud-basic' : 'basic', @@ -313,7 +295,7 @@ const loginViaConfig = () => { const config = yaml.safeLoad(kibanaDevYml); // programmatically authenticate without interacting with the Kibana login page - cy.request({ + request({ body: { providerType: 'basic', providerName: 'basic', diff --git a/x-pack/plugins/security_solution/public/management/cypress/tasks/perform_user_actions.ts b/x-pack/plugins/security_solution/public/management/cypress/tasks/perform_user_actions.ts index 69c7bd369cdeb..cac6e42720bfd 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/tasks/perform_user_actions.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/tasks/perform_user_actions.ts @@ -25,7 +25,7 @@ const performAction = (action: FormAction) => { if (action.customSelector) { element = cy.get(action.customSelector); } else { - element = cy.getBySel(action.selector || ''); + element = cy.getByTestSubj(action.selector || ''); } if (action.type === 'click') { diff --git a/x-pack/plugins/security_solution/public/management/cypress/tsconfig.json b/x-pack/plugins/security_solution/public/management/cypress/tsconfig.json index a385fa4c78ec6..7af7e1ce057ef 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/tsconfig.json +++ b/x-pack/plugins/security_solution/public/management/cypress/tsconfig.json @@ -1,7 +1,8 @@ { "extends": "../../../../../../tsconfig.base.json", "include": [ - "**/*" + "**/*", + "../cypress_endpoint.config.ts" ], "exclude": [ "target/**/*" @@ -26,5 +27,6 @@ "@kbn/securitysolution-list-constants", "@kbn/fleet-plugin", "@kbn/securitysolution-io-ts-list-types", + "@kbn/cypress-config", ] } diff --git a/x-pack/plugins/security_solution/public/management/cypress_endpoint.config.ts b/x-pack/plugins/security_solution/public/management/cypress_endpoint.config.ts index d1487b6d990d6..3ee57275cc7fd 100644 --- a/x-pack/plugins/security_solution/public/management/cypress_endpoint.config.ts +++ b/x-pack/plugins/security_solution/public/management/cypress_endpoint.config.ts @@ -6,6 +6,8 @@ */ import { defineCypressConfig } from '@kbn/cypress-config'; +// eslint-disable-next-line @kbn/imports/no_boundary_crossing +import { dataLoaders } from './cypress/support/data_loaders'; // eslint-disable-next-line import/no-default-export export default defineCypressConfig({ @@ -37,5 +39,8 @@ export default defineCypressConfig({ supportFile: 'public/management/cypress/support/e2e.ts', specPattern: 'public/management/cypress/e2e/endpoint/*.cy.{js,jsx,ts,tsx}', experimentalRunAllSpecs: true, + setupNodeEvents(on: Cypress.PluginEvents, config: Cypress.PluginConfigOptions) { + dataLoaders(on, config); + }, }, }); diff --git a/x-pack/plugins/security_solution/public/management/hooks/use_test_id_generator.ts b/x-pack/plugins/security_solution/public/management/hooks/use_test_id_generator.ts index 35ba460b7e876..e8048d110224f 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/use_test_id_generator.ts +++ b/x-pack/plugins/security_solution/public/management/hooks/use_test_id_generator.ts @@ -16,6 +16,7 @@ import { useCallback } from 'react'; * @example * // `props['data-test-subj'] = 'abc'; * const getTestId = useTestIdGenerator(props['data-test-subj']); + * getTestId(); // abc * getTestId('body'); // abc-body * getTestId('some-other-ui-section'); // abc-some-other-ui-section * @@ -24,11 +25,11 @@ import { useCallback } from 'react'; * const getTestId = useTestIdGenerator(props['data-test-subj']); * getTestId('body'); // undefined */ -export const useTestIdGenerator = (prefix?: string): ((suffix: string) => string | undefined) => { +export const useTestIdGenerator = (prefix?: string): ((suffix?: string) => string | undefined) => { return useCallback( - (suffix: string): string | undefined => { + (suffix: string = ''): string | undefined => { if (prefix) { - return `${prefix}-${suffix}`; + return `${prefix}${suffix ? `-${suffix}` : ''}`; } }, [prefix] diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx index 1a05a0f48f050..0a6c82ff452a2 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx @@ -12,7 +12,7 @@ import { useUserPrivileges } from '../../../../../common/components/user_privile import { useWithShowEndpointResponder } from '../../../../hooks'; import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features'; import { APP_UI_ID } from '../../../../../../common/constants'; -import { getEndpointDetailsPath } from '../../../../common/routing'; +import { getEndpointDetailsPath, getEndpointListPath } from '../../../../common/routing'; import type { HostMetadata, MaybeImmutable } from '../../../../../../common/endpoint/types'; import { useEndpointSelector } from './hooks'; import { agentPolicies, uiQueryParams } from '../../store/selectors'; @@ -236,6 +236,12 @@ export const useEndpointActionItems = ( agentId: fleetAgentId, })[1] }?openReassignFlyout=true`, + state: { + onDoneNavigateTo: [ + APP_UI_ID, + { path: getEndpointListPath({ name: 'endpointList' }) }, + ], + }, }, href: `${getAppUrl({ appId: 'fleet' })}${ pagePathGetters.agent_details({ diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/alerts_by_status.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/alerts_by_status.tsx index e3ab3b292aaba..ea81ed56eb8e0 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/alerts_by_status.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/alerts_by_status.tsx @@ -291,7 +291,7 @@ export const AlertsByStatus = ({ /> ) : ( Omit = () => { ...noResponseActionsRole.kibana[0], feature: { ...noResponseActionsRole.kibana[0].feature, - siem: ['minimal_all', 'actions_log_management_read'], + siem: [ + 'minimal_all', + + 'policy_management_read', + + 'trusted_applications_read', + 'event_filters_read', + 'host_isolation_exceptions_read', + 'blocklist_all', + + 'actions_log_management_read', + ], }, }, ], diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/endpoint_operations_analyst.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/endpoint_operations_analyst.ts index 17f43b3d6abb8..afc8941041128 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/endpoint_operations_analyst.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/endpoint_operations_analyst.ts @@ -19,9 +19,17 @@ export const getEndpointOperationsAnalyst: () => Omit = () => { ...noResponseActionsRole.kibana[0].feature, siem: [ 'minimal_all', - 'actions_log_management_all', + + 'policy_management_all', + + 'trusted_applications_all', + 'event_filters_all', + 'host_isolation_exceptions_all', + 'blocklist_all', + 'host_isolation_all', 'process_operations_all', + 'actions_log_management_all', ], }, }, diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/endpoint_security_policy_manager.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/endpoint_security_policy_manager.ts index 0a9679b8dbfd6..d4a94b3063a59 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/endpoint_security_policy_manager.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/endpoint_security_policy_manager.ts @@ -18,13 +18,14 @@ export const getEndpointSecurityPolicyManager: () => Omit = () => feature: { ...noResponseActionsRole.kibana[0].feature, siem: [ - 'blocklist_all', - 'endpoint_list_all', - 'event_filters_all', - 'host_isolation_exceptions_all', 'minimal_all', + 'policy_management_all', + 'trusted_applications_all', + 'event_filters_all', + 'host_isolation_exceptions_all', + 'blocklist_all', ], }, }, diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/hunter.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/hunter.ts index 808df4307dcac..ccec0ea948bc3 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/hunter.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/hunter.ts @@ -19,13 +19,17 @@ export const getHunter: () => Omit = () => { ...noResponseActionsRole.kibana[0].feature, siem: [ 'minimal_all', - 'actions_log_management_all', + + 'policy_management_read', + + 'trusted_applications_read', + 'event_filters_read', + 'host_isolation_exceptions_read', + 'blocklist_all', + 'host_isolation_all', 'process_operations_all', - 'trusted_applications_all', - 'event_filters_all', - 'host_isolation_exceptions_all', - 'blocklist_all', + 'actions_log_management_all', ], }, }, diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/platform_engineer.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/platform_engineer.ts index d0e3f0609dcb0..10053a9adc4fb 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/platform_engineer.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/platform_engineer.ts @@ -17,7 +17,18 @@ export const getPlatformEngineer: () => Omit = () => { ...noResponseActionsRole.kibana[0], feature: { ...noResponseActionsRole.kibana[0].feature, - siem: ['minimal_all', 'actions_log_management_read'], + siem: [ + 'minimal_all', + + 'policy_management_all', + + 'trusted_applications_all', + 'event_filters_all', + 'host_isolation_exceptions_all', + 'blocklist_all', + + 'actions_log_management_read', + ], }, }, ], diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/soc_manager.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/soc_manager.ts index ffa2c1435a118..532e7271e4b6b 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/soc_manager.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/soc_manager.ts @@ -19,9 +19,17 @@ export const getSocManager: () => Omit = () => { ...noResponseActionsRole.kibana[0].feature, siem: [ 'minimal_all', - 'actions_log_management_all', + + 'policy_management_all', + + 'trusted_applications_all', + 'event_filters_all', + 'host_isolation_exceptions_all', + 'blocklist_all', + 'host_isolation_all', 'process_operations_all', + 'actions_log_management_all', ], }, }, diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/thread_intelligence_analyst.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/threat_intelligence_analyst.ts similarity index 82% rename from x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/thread_intelligence_analyst.ts rename to x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/threat_intelligence_analyst.ts index 76055e2b8cbd7..884b9412d06d6 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/thread_intelligence_analyst.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/threat_intelligence_analyst.ts @@ -8,7 +8,7 @@ import type { Role } from '@kbn/security-plugin/common'; import { getNoResponseActionsRole } from './without_response_actions_role'; -export const getThreadIntelligenceAnalyst: () => Omit = () => { +export const getThreatIntelligenceAnalyst: () => Omit = () => { const noResponseActionsRole = getNoResponseActionsRole(); return { ...noResponseActionsRole, @@ -17,7 +17,7 @@ export const getThreadIntelligenceAnalyst: () => Omit = () => { ...noResponseActionsRole.kibana[0], feature: { ...noResponseActionsRole.kibana[0].feature, - siem: ['minimal_all', 'actions_log_management_read'], + siem: ['minimal_all', 'blocklist_all', 'actions_log_management_read'], }, }, ], diff --git a/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator_script.ts b/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator_script.ts index b944835e0c8da..c0be16370ddcc 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator_script.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator_script.ts @@ -30,13 +30,13 @@ import { getEndpointSecurityPolicyManager } from './common/roles_users/endpoint_ import { getHunter } from './common/roles_users/hunter'; import { getPlatformEngineer } from './common/roles_users/platform_engineer'; import { getSocManager } from './common/roles_users/soc_manager'; -import { getThreadIntelligenceAnalyst } from './common/roles_users/thread_intelligence_analyst'; +import { getThreatIntelligenceAnalyst } from './common/roles_users/threat_intelligence_analyst'; const rolesMapping: { [id: string]: Omit } = { t1Analyst: getT1Analyst(), t2Analyst: getT2Analyst(), hunter: getHunter(), - threadIntelligenceAnalyst: getThreadIntelligenceAnalyst(), + threatIntelligenceAnalyst: getThreatIntelligenceAnalyst(), socManager: getSocManager(), platformEngineer: getPlatformEngineer(), endpointOperationsAnalyst: getEndpointOperationsAnalyst(), diff --git a/x-pack/plugins/security_solution/server/config.ts b/x-pack/plugins/security_solution/server/config.ts index 5a4d18edda11c..26f1be4f014b3 100644 --- a/x-pack/plugins/security_solution/server/config.ts +++ b/x-pack/plugins/security_solution/server/config.ts @@ -26,7 +26,7 @@ export const configSchema = schema.object({ /** * This is used within the merge strategies: - * server/lib/detection_engine/signals/source_fields_merging + * server/lib/detection_engine/rule_types/utils/source_fields_merging * * For determining which strategy for merging "fields" and "_source" together to get * runtime fields, constant keywords, etc... @@ -44,7 +44,7 @@ export const configSchema = schema.object({ /** * This is used within the merge strategies: - * server/lib/detection_engine/signals/source_fields_merging + * server/lib/detection_engine/rule_types/utils/source_fields_merging * * For determining if we need to ignore particular "fields" and not merge them with "_source" such as * runtime fields, constant keywords, etc... diff --git a/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts b/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts index d1e236f897ade..0f29e1d46801d 100644 --- a/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts +++ b/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts @@ -12,7 +12,7 @@ import type { PluginStartContract as CasesPluginStartContract, } from '@kbn/cases-plugin/server'; import type { SecurityPluginStart } from '@kbn/security-plugin/server'; -import type { FleetStartContract } from '@kbn/fleet-plugin/server'; +import type { FleetStartContract, MessageSigningServiceInterface } from '@kbn/fleet-plugin/server'; import type { PluginStartContract as AlertsPluginStartContract } from '@kbn/alerting-plugin/server'; import { ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID } from '@kbn/securitysolution-list-constants'; import { @@ -61,6 +61,7 @@ export interface EndpointAppContextServiceStartContract { cases: CasesPluginStartContract | undefined; featureUsageService: FeatureUsageService; experimentalFeatures: ExperimentalFeatures; + messageSigningService: MessageSigningServiceInterface | undefined; } /** @@ -223,4 +224,12 @@ export class EndpointAppContextService { return this.startDependencies.exceptionListsClient; } + + public getMessageSigningService(): MessageSigningServiceInterface { + if (!this.startDependencies?.messageSigningService) { + throw new EndpointAppContentServicesNotStartedError(); + } + + return this.startDependencies.messageSigningService; + } } diff --git a/x-pack/plugins/security_solution/server/endpoint/mocks.ts b/x-pack/plugins/security_solution/server/endpoint/mocks.ts index be0738c5f0288..7b57a384e0e10 100644 --- a/x-pack/plugins/security_solution/server/endpoint/mocks.ts +++ b/x-pack/plugins/security_solution/server/endpoint/mocks.ts @@ -33,6 +33,7 @@ import { createMockAgentPolicyService, createMockAgentService, createMockPackageService, + createMessageSigningServiceMock, } from '@kbn/fleet-plugin/server/mocks'; // A TS error (TS2403) is thrown when attempting to export the mock function below from Cases // plugin server `index.ts`. Its unclear what is actually causing the error. Since this is a Mock @@ -176,6 +177,7 @@ export const createMockEndpointAppContextServiceStartContract = }, featureUsageService: createFeatureUsageServiceMock(), experimentalFeatures: createMockConfig().experimentalFeatures, + messageSigningService: createMessageSigningServiceMock(), }; }; diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts index 0c89c183f0835..42e337efba2d9 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts @@ -108,6 +108,12 @@ describe('Response actions', () => { const routerMock = httpServiceMock.createRouter(); mockResponse = httpServerMock.createResponseFactory(); const startContract = createMockEndpointAppContextServiceStartContract(); + (startContract.messageSigningService?.sign as jest.Mock).mockImplementation(() => { + return { + data: 'thisisthedata', + signature: 'thisisasignature', + }; + }); endpointAppContextService = new EndpointAppContextService(); const mockSavedObjectClient = savedObjectsClientMock.create(); @@ -637,6 +643,35 @@ describe('Response actions', () => { expect(responseBody.action).toBeUndefined(); }); + it('signs the action', async () => { + const ctx = await callRoute( + ISOLATE_HOST_ROUTE_V2, + { + body: { endpoint_ids: ['XYZ'] }, + }, + { endpointDsExists: true } + ); + + const indexDoc = ctx.core.elasticsearch.client.asInternalUser.index; + const actionDocs: [ + { index: string; body?: LogsEndpointAction }, + { index: string; body?: EndpointAction } + ] = [ + indexDoc.mock.calls[0][0] as estypes.IndexRequest, + indexDoc.mock.calls[1][0] as estypes.IndexRequest, + ]; + + expect(actionDocs[1].index).toEqual(AGENT_ACTIONS_INDEX); + expect(actionDocs[1].body?.signed).toEqual({ + data: 'thisisthedata', + signature: 'thisisasignature', + }); + + expect(mockResponse.ok).toBeCalled(); + const responseBody = mockResponse.ok.mock.calls[0][0]?.body as ResponseActionApiResponse; + expect(responseBody.action).toBeTruthy(); + }); + it('handles errors', async () => { const ErrMessage = 'Uh oh!'; await callRoute( diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.ts index 559cd0009a4e2..b4197bb947d93 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.ts @@ -335,21 +335,36 @@ function responseActionRequestHandler( { index: AGENT_ACTIONS_INDEX, - body: { - ...doc.EndpointActions, - '@timestamp': doc['@timestamp'], - agents, - timeout: 300, // 5 minutes - user_id: doc.user.id, - }, + body: signedFleetActionDoc, refresh: 'wait_for', }, - { meta: true } + { + meta: true, + } ); if (fleetActionIndexResult.statusCode !== 201) { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/get_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/get_signals.ts index 5963cc0cb2211..dda3163c9ba14 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/get_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/get_signals.ts @@ -6,7 +6,7 @@ */ import type { ElasticsearchClient } from '@kbn/core/server'; -import type { SignalSearchResponse, SignalSource } from '../../../signals/types'; +import type { SignalSearchResponse, SignalSource } from '../../../rule_types/types'; import { buildSignalsSearchQuery } from './build_signals_query'; interface GetSignalsParams { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/legacy_rules_notification_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/legacy_rules_notification_alert_type.test.ts index ef6b05474ff47..04a892427774e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/legacy_rules_notification_alert_type.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/legacy_rules_notification_alert_type.test.ts @@ -20,7 +20,7 @@ import { sampleDocSearchResultsNoSortIdNoVersion, sampleDocSearchResultsWithSortId, sampleEmptyDocSearchResults, -} from '../../../signals/__mocks__/es_results'; +} from '../../../rule_types/__mocks__/es_results'; import { DEFAULT_RULE_NOTIFICATION_QUERY_SIZE } from '../../../../../../common/constants'; import { getQueryRuleParams } from '../../../rule_schema/mocks'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/legacy_rules_notification_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/legacy_rules_notification_alert_type.ts index 2bb349f6f070b..71269841bd7a4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/legacy_rules_notification_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/legacy_rules_notification_alert_type.ts @@ -17,8 +17,8 @@ import { import type { LegacyNotificationAlertTypeDefinition } from './legacy_types'; // eslint-disable-next-line no-restricted-imports import { legacyRulesNotificationParams } from './legacy_types'; -import type { AlertAttributes } from '../../../signals/types'; -import { siemRuleActionGroups } from '../../../signals/siem_rule_action_groups'; +import type { AlertAttributes } from '../../../rule_types/types'; +import { siemRuleActionGroups } from '../../../rule_types/utils/siem_rule_action_groups'; import { scheduleNotificationActions } from './schedule_notification_actions'; import { getNotificationResultsLink } from './utils'; import { getSignals } from './get_signals'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/schedule_notification_actions.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/schedule_notification_actions.ts index 8ca2bc0a4fb01..dd6ade149533f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/schedule_notification_actions.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/schedule_notification_actions.ts @@ -14,7 +14,7 @@ import { isThresholdRule } from '../../../../../../common/detection_engine/utils import { expandDottedObject } from '../../../../../../common/utils/expand_dotted'; import type { RuleParams } from '../../../rule_schema'; import aadFieldConversion from '../../../routes/index/signal_aad_mapping.json'; -import { isDetectionAlert } from '../../../signals/utils'; +import { isDetectionAlert } from '../../../rule_types/utils/utils'; import type { DetectionAlert } from '../../../../../../common/detection_engine/schemas/alerts'; export type NotificationRuleTypeParams = RuleParams & { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/schedule_throttle_notification_actions.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/schedule_throttle_notification_actions.ts index 98f5ee9d42557..27ffd575029c5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/schedule_throttle_notification_actions.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/schedule_throttle_notification_actions.ts @@ -14,7 +14,7 @@ import { DEFAULT_RULE_NOTIFICATION_QUERY_SIZE } from '../../../../../../common/c import { getSignals } from './get_signals'; import type { NotificationRuleTypeParams } from './schedule_notification_actions'; import { scheduleNotificationActions } from './schedule_notification_actions'; -import type { AlertAttributes } from '../../../signals/types'; +import type { AlertAttributes } from '../../../rule_types/types'; interface ScheduleThrottledNotificationActionsOptions { id: SavedObject['id']; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/utils.test.ts index cb3c4e0d4bd41..a25141635b518 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/utils.test.ts @@ -7,7 +7,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { loggingSystemMock } from '@kbn/core/server/mocks'; -import type { SignalSource } from '../../../signals/types'; +import type { SignalSource } from '../../../rule_types/types'; import { deconflictSignalsAndResults, getNotificationResultsLink } from './utils'; describe('utils', () => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/utils.ts index 9eef538efe859..717e40e3e6d5f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/utils.ts @@ -7,7 +7,7 @@ import type { Logger } from '@kbn/core/server'; import { APP_PATH } from '../../../../../../common/constants'; -import type { SignalSearchResponse } from '../../../signals/types'; +import type { SignalSearchResponse } from '../../../rule_types/types'; export const getNotificationResultsLink = ({ kibanaSiemAppUrl = APP_PATH, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/import_rules/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/import_rules/route.ts index ddcbf2cbdd6e4..c4490e418b859 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/import_rules/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/import_rules/route.ts @@ -135,6 +135,7 @@ export const importRulesRoute = ( success: actionConnectorSuccess, warnings: actionConnectorWarnings, errors: actionConnectorErrors, + rulesWithMigratedActions, } = await importRuleActionConnectors({ actionConnectors, actionsClient, @@ -142,9 +143,12 @@ export const importRulesRoute = ( rules: migratedParsedObjectsWithoutDuplicateErrors, overwrite: request.query.overwrite_action_connectors, }); + + // rulesWithMigratedActions: Is returened only in case connectors were exorted from different namesapce and the + // original rules actions' ids were replaced with new destinationIds const parsedRules = actionConnectorErrors.length ? [] - : migratedParsedObjectsWithoutDuplicateErrors; + : rulesWithMigratedActions || migratedParsedObjectsWithoutDuplicateErrors; // gather all exception lists that the imported rules reference const foundReferencedExceptionLists = await getReferencedExceptionLists({ @@ -166,7 +170,6 @@ export const importRulesRoute = ( existingLists: foundReferencedExceptionLists, allowMissingConnectorSecrets: !!actionConnectors.length, }); - const errorsResp = importRuleResponse.filter((resp) => isBulkError(resp)) as BulkError[]; const successes = importRuleResponse.filter((resp) => { if (isImportRegular(resp)) { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/action_connectors/import_rule_action_connectors.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/action_connectors/import_rule_action_connectors.test.ts index 455274006297e..2f81d1284eb53 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/action_connectors/import_rule_action_connectors.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/action_connectors/import_rule_action_connectors.test.ts @@ -36,7 +36,7 @@ const actionsClient = actionsClientMock.create(); actionsClient.getAll.mockResolvedValue([]); const core = coreMock.createRequestHandlerContext(); -describe('checkRuleExceptionReferences', () => { +describe('importRuleActionConnectors', () => { beforeEach(() => { jest.clearAllMocks(); }); @@ -55,7 +55,7 @@ describe('checkRuleExceptionReferences', () => { const res = await importRuleActionConnectors({ actionConnectors, actionsClient, - actionsImporter: actionsImporter2() as never, + actionsImporter: actionsImporter2(), rules, overwrite: false, }); @@ -82,7 +82,6 @@ describe('checkRuleExceptionReferences', () => { import: jest.fn().mockResolvedValue({ success: true, successCount: 1, - successResults: [], errors: [], warnings: [], }), @@ -92,7 +91,7 @@ describe('checkRuleExceptionReferences', () => { const res = await importRuleActionConnectors({ actionConnectors, actionsClient, - actionsImporter: actionsImporter() as never, + actionsImporter: actionsImporter(), rules, overwrite: false, }); @@ -100,7 +99,6 @@ describe('checkRuleExceptionReferences', () => { expect(res).toEqual({ success: true, successCount: 1, - successResults: [], errors: [], warnings: [], }); @@ -110,7 +108,6 @@ describe('checkRuleExceptionReferences', () => { import: jest.fn().mockResolvedValue({ success: true, successCount: 1, - successResults: [], errors: [], warnings: [], }), @@ -143,7 +140,7 @@ describe('checkRuleExceptionReferences', () => { const res = await importRuleActionConnectors({ actionConnectors, actionsClient, - actionsImporter: actionsImporter() as never, + actionsImporter: actionsImporter(), rules: ruleWith2Connectors, overwrite: false, }); @@ -151,7 +148,6 @@ describe('checkRuleExceptionReferences', () => { expect(res).toEqual({ success: true, successCount: 1, - successResults: [], errors: [], warnings: [], }); @@ -163,7 +159,7 @@ describe('checkRuleExceptionReferences', () => { const res = await importRuleActionConnectors({ actionConnectors: [], actionsClient, - actionsImporter: actionsImporter() as never, + actionsImporter: actionsImporter(), rules, overwrite: false, }); @@ -190,7 +186,7 @@ describe('checkRuleExceptionReferences', () => { const res = await importRuleActionConnectors({ actionConnectors: [], actionsClient, - actionsImporter: actionsImporter() as never, + actionsImporter: actionsImporter(), rules: [ { ...getImportRulesSchemaMock(), @@ -235,7 +231,7 @@ describe('checkRuleExceptionReferences', () => { const res = await importRuleActionConnectors({ actionConnectors: [], actionsClient, - actionsImporter: actionsImporter() as never, + actionsImporter: actionsImporter(), rules: [ { ...getImportRulesSchemaMock(), @@ -285,18 +281,17 @@ describe('checkRuleExceptionReferences', () => { import: jest.fn().mockResolvedValue({ success: true, successCount: 2, - successResults: [], errors: [], warnings: [], }), }); const actionsImporter2 = core.savedObjects.getImporter; - const actionsImporter2Import = actionsImporter2().import; + const actionsImporter2Importer = actionsImporter2(); const res = await importRuleActionConnectors({ actionConnectors, actionsClient, - actionsImporter: actionsImporter2Import as never, + actionsImporter: actionsImporter2Importer, rules: rulesWithoutActions, overwrite: false, }); @@ -307,7 +302,7 @@ describe('checkRuleExceptionReferences', () => { errors: [], warnings: [], }); - expect(actionsImporter2Import).not.toBeCalled(); + expect(actionsImporter2Importer.import).not.toBeCalled(); }); it('should skip importing the action-connectors if all connectors have been imported/created before', async () => { @@ -322,12 +317,12 @@ describe('checkRuleExceptionReferences', () => { }, ]); const actionsImporter2 = core.savedObjects.getImporter; - const actionsImporter2Import = actionsImporter2().import; + const actionsImporter2Importer = actionsImporter2(); const res = await importRuleActionConnectors({ actionConnectors, actionsClient, - actionsImporter: actionsImporter2Import as never, + actionsImporter: actionsImporter2Importer, rules, overwrite: false, }); @@ -338,7 +333,7 @@ describe('checkRuleExceptionReferences', () => { errors: [], warnings: [], }); - expect(actionsImporter2Import).not.toBeCalled(); + expect(actionsImporter2Importer.import).not.toBeCalled(); }); it('should not skip importing the action-connectors if all connectors have been imported/created before when overwrite is true', async () => { @@ -346,7 +341,6 @@ describe('checkRuleExceptionReferences', () => { import: jest.fn().mockResolvedValue({ success: true, successCount: 1, - successResults: [], errors: [], warnings: [], }), @@ -367,7 +361,7 @@ describe('checkRuleExceptionReferences', () => { const res = await importRuleActionConnectors({ actionConnectors, actionsClient, - actionsImporter: actionsImporter() as never, + actionsImporter: actionsImporter(), rules, overwrite: true, }); @@ -377,7 +371,194 @@ describe('checkRuleExceptionReferences', () => { successCount: 1, errors: [], warnings: [], - successResults: [], + }); + }); + + it('should import one rule with connector successfully even if it was exported from different namespaces by generating destinationId and replace the old actionId with it', async () => { + const successResults = [ + { + destinationId: '72cab9bb-535f-45dd-b9c2-5bc1bc0db96b', + id: 'cabc78e0-9031-11ed-b076-53cc4d57aaf1', + meta: { title: 'Connector: [anotherSpaceSlack]', icon: undefined }, + type: 'action', + }, + ]; + core.savedObjects.getImporter = jest.fn().mockReturnValueOnce({ + import: jest.fn().mockResolvedValue({ + success: true, + successCount: 1, + successResults, + errors: [], + warnings: [], + }), + }); + const actionsImporter = core.savedObjects.getImporter; + + actionsClient.getAll.mockResolvedValue([]); + + const res = await importRuleActionConnectors({ + actionConnectors, + actionsClient, + actionsImporter: actionsImporter(), + rules, + overwrite: false, + }); + const rulesWithMigratedActions = [ + { + actions: [ + { + action_type_id: '.webhook', + group: 'default', + id: '72cab9bb-535f-45dd-b9c2-5bc1bc0db96b', + params: {}, + }, + ], + description: 'some description', + language: 'kuery', + name: 'Query with a rule id', + query: 'user.name: root or user.name: admin', + risk_score: 55, + rule_id: 'rule-1', + severity: 'high', + type: 'query', + }, + ]; + + expect(res).toEqual({ + success: true, + successCount: 1, + errors: [], + warnings: [], + rulesWithMigratedActions, + }); + }); + + it('should import multiple rules with connectors successfully even if they were exported from different namespaces by generating destinationIds and replace the old actionIds with them', async () => { + const multipleRules = [ + { + ...getImportRulesSchemaMock(), + actions: [ + { + group: 'default', + id: 'cabc78e0-9031-11ed-b076-53cc4d57aaf1', + action_type_id: '.webhook', + params: {}, + }, + ], + }, + { + ...getImportRulesSchemaMock(), + rule_id: 'rule_2', + id: '0abc78e0-7031-11ed-b076-53cc4d57aaf1', + actions: [ + { + group: 'default', + id: '11abc78e0-9031-11ed-b076-53cc4d57aaw', + action_type_id: '.index', + params: {}, + }, + ], + }, + ]; + const successResults = [ + { + destinationId: '72cab9bb-535f-45dd-b9c2-5bc1bc0db96b', + id: 'cabc78e0-9031-11ed-b076-53cc4d57aaf1', + meta: { title: 'Connector: [anotherSpaceSlack]', icon: undefined }, + type: 'action', + }, + { + destinationId: '892cab9bb-535f-45dd-b9c2-5bc1bc0db96', + id: '11abc78e0-9031-11ed-b076-53cc4d57aaw', + meta: { title: 'Connector: [anotherSpaceSlack]', icon: undefined }, + type: 'action', + }, + ]; + core.savedObjects.getImporter = jest.fn().mockReturnValueOnce({ + import: jest.fn().mockResolvedValue({ + success: true, + successCount: 1, + successResults, + errors: [], + warnings: [], + }), + }); + const actionsImporter = core.savedObjects.getImporter; + const actionConnectorsWithIndex = [ + ...actionConnectors, + { + id: '0abc78e0-7031-11ed-b076-53cc4d57aaf1', + type: 'action', + updated_at: '2023-01-25T14:35:52.852Z', + created_at: '2023-01-25T14:35:52.852Z', + version: 'WzUxNTksMV0=', + attributes: { + actionTypeId: '.webhook', + name: 'webhook', + isMissingSecrets: false, + config: {}, + secrets: {}, + }, + references: [], + migrationVersion: { action: '8.3.0' }, + coreMigrationVersion: '8.7.0', + }, + ]; + actionsClient.getAll.mockResolvedValue([]); + + const res = await importRuleActionConnectors({ + actionConnectors: actionConnectorsWithIndex, + actionsClient, + actionsImporter: actionsImporter(), + rules: multipleRules, + overwrite: false, + }); + const rulesWithMigratedActions = [ + { + actions: [ + { + action_type_id: '.webhook', + group: 'default', + id: '72cab9bb-535f-45dd-b9c2-5bc1bc0db96b', + params: {}, + }, + ], + description: 'some description', + language: 'kuery', + name: 'Query with a rule id', + query: 'user.name: root or user.name: admin', + risk_score: 55, + rule_id: 'rule-1', + severity: 'high', + type: 'query', + }, + { + actions: [ + { + action_type_id: '.index', + group: 'default', + id: '892cab9bb-535f-45dd-b9c2-5bc1bc0db96', + params: {}, + }, + ], + description: 'some description', + language: 'kuery', + name: 'Query with a rule id', + id: '0abc78e0-7031-11ed-b076-53cc4d57aaf1', + rule_id: 'rule_2', + query: 'user.name: root or user.name: admin', + risk_score: 55, + severity: 'high', + type: 'query', + }, + ]; + + expect(res).toEqual({ + success: true, + successCount: 1, + errors: [], + warnings: [], + rulesWithMigratedActions, }); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/action_connectors/import_rule_action_connectors.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/action_connectors/import_rule_action_connectors.ts index 3d2fc9fb7c6b6..dda1a28192eef 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/action_connectors/import_rule_action_connectors.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/action_connectors/import_rule_action_connectors.ts @@ -9,6 +9,7 @@ import { Readable } from 'stream'; import type { SavedObjectsImportResponse } from '@kbn/core-saved-objects-common'; import type { SavedObject } from '@kbn/core-saved-objects-server'; +import type { RuleToImport } from '../../../../../../../common/detection_engine/rule_management'; import type { WarningSchema } from '../../../../../../../common/detection_engine/schemas/response'; import { checkIfActionsHaveMissingConnectors, @@ -17,6 +18,7 @@ import { handleActionsHaveNoConnectors, mapSOErrorToRuleError, returnErroredImportResult, + updateRuleActionsWithMigratedResults, } from './utils'; import type { ImportRuleActionConnectorsParams, ImportRuleActionConnectorsResult } from './types'; @@ -71,12 +73,21 @@ export const importRuleActionConnectors = async ({ overwrite, createNewCopies: false, }); + /* + // When a connector is exported from one namespace and imported to another, it does not result in an error, but instead a new object is created with + // new destination id and id will have the old origin id, so in order to be able to use the newly generated Connectors id, this util is used to swap the old id with the + // new destination Id + */ + let rulesWithMigratedActions: Array | undefined; + if (successResults?.some((res) => res.destinationId)) + rulesWithMigratedActions = updateRuleActionsWithMigratedResults(rules, successResults); + return { success, successCount, - successResults, errors: errors ? mapSOErrorToRuleError(errors) : [], warnings: (warnings as WarningSchema[]) || [], + rulesWithMigratedActions, }; } catch (error) { return returnErroredImportResult(error); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/action_connectors/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/action_connectors/types.ts index 8ba6c41c42dde..3d9471016a859 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/action_connectors/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/action_connectors/types.ts @@ -6,10 +6,7 @@ */ import type { ISavedObjectsImporter, SavedObject } from '@kbn/core-saved-objects-server'; import type { ActionsClient } from '@kbn/actions-plugin/server'; -import type { - SavedObjectsImportFailure, - SavedObjectsImportSuccess, -} from '@kbn/core-saved-objects-common'; +import type { SavedObjectsImportFailure } from '@kbn/core-saved-objects-common'; import type { RuleToImport } from '../../../../../../../common/detection_engine/rule_management'; import type { WarningSchema } from '../../../../../../../common/detection_engine/schemas/response'; import type { BulkError } from '../../../../routes/utils'; @@ -17,9 +14,9 @@ import type { BulkError } from '../../../../routes/utils'; export interface ImportRuleActionConnectorsResult { success: boolean; successCount: number; - successResults?: SavedObjectsImportSuccess[]; errors: BulkError[] | []; warnings: WarningSchema[] | []; + rulesWithMigratedActions?: Array; } export interface ImportRuleActionConnectorsParams { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/action_connectors/utils/index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/action_connectors/utils/index.ts index 6d9b3b9da9e6d..caea8ad664f01 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/action_connectors/utils/index.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/action_connectors/utils/index.ts @@ -5,7 +5,10 @@ * 2.0. */ import { pick } from 'lodash'; -import type { SavedObjectsImportFailure } from '@kbn/core-saved-objects-common'; +import type { + SavedObjectsImportFailure, + SavedObjectsImportSuccess, +} from '@kbn/core-saved-objects-common'; import type { SavedObject } from '@kbn/core-saved-objects-server'; import type { ActionsClient } from '@kbn/actions-plugin/server'; import type { BulkError } from '../../../../../routes/utils'; @@ -127,3 +130,43 @@ export const checkIfActionsHaveMissingConnectors = ( } return null; }; + +export const mapActionIdToNewDestinationId = ( + connectorsImportResult: SavedObjectsImportSuccess[] +) => { + return connectorsImportResult.reduce( + (acc: { [actionId: string]: string }, { destinationId, id }) => { + acc[id] = destinationId || id; + return acc; + }, + {} + ); +}; + +export const swapNonDefaultSpaceIdWithDestinationId = ( + rule: RuleToImport, + actionIdDestinationIdLookup: { [actionId: string]: string } +) => { + return rule.actions?.map((action) => { + const destinationId = actionIdDestinationIdLookup[action.id]; + return { ...action, id: destinationId }; + }); +}; +/* +// When a connector is exported from one namespace and imported to another, it does not result in an error, but instead a new object is created with +// new destination id and id will have the old origin id, so in order to be able to use the newly generated Connectors id, this util is used to swap the old id with the +// new destination Id +*/ +export const updateRuleActionsWithMigratedResults = ( + rules: Array, + connectorsImportResult: SavedObjectsImportSuccess[] +): Array => { + const actionIdDestinationIdLookup = mapActionIdToNewDestinationId(connectorsImportResult); + return rules.map((rule) => { + if (rule instanceof Error) return rule; + return { + ...rule, + actions: swapNonDefaultSpaceIdWithDestinationId(rule, actionIdDestinationIdLookup), + }; + }); +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/create_rule_execution_summary.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/create_rule_execution_summary.ts index c12dbf9c36d9c..3818ca1403e63 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/create_rule_execution_summary.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/create_rule_execution_summary.ts @@ -13,41 +13,56 @@ import { ruleExecutionStatusToNumber, RuleExecutionStatus, } from '../../../../../../common/detection_engine/rule_monitoring'; + import type { RuleParams } from '../../../rule_schema'; export const createRuleExecutionSummary = ( rule: SanitizedRule | ResolvedSanitizedRule ): RuleExecutionSummary | null => { + if (!rule.monitoring) { + // In the rule type the monitoring object is optional because in some cases rules client returns a rule without it. + // For instance, when we call RulesClient.create(). Despite the fact that it's always in the rule saved object, + // even when it was just created: + // https://github.com/elastic/kibana/blob/26eddec70844ffb65e5d3521a7adb52e643c4534/x-pack/plugins/alerting/server/rules_client/methods/create.ts#L155 + return null; + } + + // Data that we need to create rule execution summary is stored in two different "last run" objects within a rule. + + // This last run object is internal to Kibana server and is not exposed via any public HTTP API. + // Alerting Framework keeps it for itself and provides via the RulesClient for solutions. + const lastRunInternal = rule.monitoring.run.last_run; + // This last run object is public - it is exposed via the public Alerting HTTP API. + const lastRunPublic = rule.lastRun; + if (rule.running) { return { last_execution: { - date: new Date().toISOString(), - message: '', - metrics: {}, + date: lastRunInternal.timestamp, status: RuleExecutionStatus.running, status_order: ruleExecutionStatusToNumber(RuleExecutionStatus.running), + message: '', + metrics: {}, }, }; } - if (!rule.lastRun) { + if (!lastRunPublic) { return null; } - const ruleExecutionStatus = ruleLastRunOutcomeToExecutionStatus(rule.lastRun.outcome); + const ruleExecutionStatus = ruleLastRunOutcomeToExecutionStatus(lastRunPublic.outcome); return { last_execution: { - date: rule.monitoring?.run.last_run?.timestamp ?? new Date().toISOString(), + date: lastRunInternal.timestamp, status: ruleExecutionStatus, status_order: ruleExecutionStatusToNumber(ruleExecutionStatus), - message: rule.lastRun?.outcomeMsg?.join(' \n') ?? '', + message: lastRunPublic.outcomeMsg?.join(' \n') ?? '', metrics: { - total_indexing_duration_ms: - rule.monitoring?.run.last_run.metrics.total_indexing_duration_ms ?? undefined, - total_search_duration_ms: - rule.monitoring?.run.last_run.metrics.total_search_duration_ms ?? undefined, - execution_gap_duration_s: rule.monitoring?.run.last_run.metrics.gap_duration_s ?? undefined, + total_indexing_duration_ms: lastRunInternal.metrics.total_indexing_duration_ms ?? undefined, + total_search_duration_ms: lastRunInternal.metrics.total_search_duration_ms ?? undefined, + execution_gap_duration_s: lastRunInternal.metrics.gap_duration_s ?? undefined, }, }, }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/preview/alert_instance_factory_stub.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview/api/preview_rules/alert_instance_factory_stub.ts similarity index 96% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/preview/alert_instance_factory_stub.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview/api/preview_rules/alert_instance_factory_stub.ts index 1b947b1be08a2..f350d2a3cb35f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/preview/alert_instance_factory_stub.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview/api/preview_rules/alert_instance_factory_stub.ts @@ -11,7 +11,7 @@ import type { RuleTypeState, } from '@kbn/alerting-plugin/common'; import { Alert } from '@kbn/alerting-plugin/server/alert'; -import type { RuleParams } from '../../rule_schema'; +import type { RuleParams } from '../../../rule_schema'; export const alertInstanceFactoryStub = < TParams extends RuleParams, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/preview/preview_rule_execution_logger.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview/api/preview_rules/preview_rule_execution_logger.ts similarity index 96% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/preview/preview_rule_execution_logger.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview/api/preview_rules/preview_rule_execution_logger.ts index 978a43a29a878..46cb0e8adfbf4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/preview/preview_rule_execution_logger.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview/api/preview_rules/preview_rule_execution_logger.ts @@ -9,7 +9,7 @@ import type { IRuleExecutionLogService, RuleExecutionContext, StatusChangeArgs, -} from '../../rule_monitoring'; +} from '../../../rule_monitoring'; export interface IPreviewRuleExecutionLogger { factory: IRuleExecutionLogService['createClientForExecutors']; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview/api/preview_rules/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview/api/preview_rules/route.ts index 35c328a68f733..7ff1366b8d945 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview/api/preview_rules/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview/api/preview_rules/route.ts @@ -32,17 +32,18 @@ import type { StartPlugins, SetupPlugins } from '../../../../../plugin'; import { buildSiemResponse } from '../../../routes/utils'; import { convertCreateAPIToInternalSchema } from '../../../rule_management'; import type { RuleParams } from '../../../rule_schema'; -import { createPreviewRuleExecutionLogger } from '../../../signals/preview/preview_rule_execution_logger'; -import { parseInterval } from '../../../signals/utils'; +import { createPreviewRuleExecutionLogger } from './preview_rule_execution_logger'; +import { parseInterval } from '../../../rule_types/utils/utils'; import { buildMlAuthz } from '../../../../machine_learning/authz'; import { throwAuthzError } from '../../../../machine_learning/validation'; import { buildRouteValidation } from '../../../../../utils/build_validation/route_validation'; +import { routeLimitedConcurrencyTag } from '../../../../../utils/route_limited_concurrency_tag'; import type { SecuritySolutionPluginRouter } from '../../../../../types'; import type { RuleExecutionContext, StatusChangeArgs } from '../../../rule_monitoring'; import type { ConfigType } from '../../../../../config'; -import { alertInstanceFactoryStub } from '../../../signals/preview/alert_instance_factory_stub'; +import { alertInstanceFactoryStub } from './alert_instance_factory_stub'; import type { CreateRuleOptions, CreateSecurityRuleTypeWrapperProps, @@ -61,6 +62,7 @@ import { wrapScopedClusterClient } from './wrap_scoped_cluster_client'; import { wrapSearchSourceClient } from './wrap_search_source_client'; const PREVIEW_TIMEOUT_SECONDS = 60; +const MAX_ROUTE_CONCURRENCY = 10; export const previewRulesRoute = async ( router: SecuritySolutionPluginRouter, @@ -80,7 +82,7 @@ export const previewRulesRoute = async ( body: buildRouteValidation(previewRulesSchema), }, options: { - tags: ['access:securitySolution'], + tags: ['access:securitySolution', routeLimitedConcurrencyTag(MAX_ROUTE_CONCURRENCY)], }, }, async (context, request, response) => { 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 86966e8193158..7c12639d7efdf 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 @@ -7,7 +7,7 @@ import { getThreatMock } from '../../../../../common/detection_engine/schemas/types/threat.mock'; import { getListArrayMock } from '../../../../../common/detection_engine/schemas/types/lists.mock'; -import { getThreatMappingMock } from '../../signals/threat_mapping/build_threat_mapping_filter.mock'; +import { getThreatMappingMock } from '../../rule_types/indicator_match/threat_mapping/build_threat_mapping_filter.mock'; import type { BaseRuleParams, CompleteRule, @@ -21,7 +21,7 @@ import type { ThresholdRuleParams, } from '..'; import type { SanitizedRuleConfig } from '@kbn/alerting-plugin/common'; -import { sampleRuleGuid } from '../../signals/__mocks__/es_results'; +import { sampleRuleGuid } from '../../rule_types/__mocks__/es_results'; const getBaseRuleParams = (): BaseRuleParams => { return { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/empty_signal_source_hit.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/empty_signal_source_hit.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/empty_signal_source_hit.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/empty_signal_source_hit.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/es_results.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/es_results.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts index a76e3d3d3af7e..228cb67122a26 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts @@ -17,7 +17,7 @@ import { ruleRegistryMocks } from '@kbn/rule-registry-plugin/server/mocks'; import { eventLogServiceMock } from '@kbn/event-log-plugin/server/mocks'; import type { PluginSetupContract as AlertingPluginSetupContract } from '@kbn/alerting-plugin/server'; import type { ConfigType } from '../../../../config'; -import type { AlertAttributes } from '../../signals/types'; +import type { AlertAttributes } from '../types'; import { createRuleMock } from './rule'; import { listMock } from '@kbn/lists-plugin/server/mocks'; import type { QueryRuleParams, RuleParams } from '../../rule_schema'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/threshold.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/threshold.ts index ab6d64e69b58d..15f82e84155cb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/threshold.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/threshold.ts @@ -35,6 +35,8 @@ import { ALERT_ORIGINAL_EVENT, ALERT_THRESHOLD_RESULT, } from '../../../../../common/field_maps/field_names'; +import type { ThresholdSignalHistory } from '../threshold/types'; +import { getThresholdTermsHash } from '../threshold/utils'; export const sampleThresholdAlert = { _id: 'b3ad77a4-65bd-4c4e-89cf-13c46f54bc4d', @@ -126,3 +128,22 @@ export const sampleThresholdAlert = { 'kibana.alert.depth': 1, }, }; + +export const sampleThresholdSignalHistory = (): ThresholdSignalHistory => { + const terms = [ + { + field: 'source.ip', + value: '127.0.0.1', + }, + { + field: 'host.name', + value: 'garden-gnomes', + }, + ]; + return { + [`${getThresholdTermsHash(terms)}`]: { + terms, + lastSignalTimestamp: new Date('2020-12-17T16:28:00Z').getTime(), + }, + }; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts index 5be1d2f23df2d..38ce087fb6aae 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts @@ -21,7 +21,7 @@ import { hasReadIndexPrivileges, hasTimestampFields, isMachineLearningParams, -} from '../signals/utils'; +} from './utils/utils'; import { DEFAULT_MAX_SIGNALS, DEFAULT_SEARCH_AFTER_PAGE_SIZE } from '../../../../common/constants'; import type { CreateSecurityRuleTypeWrapper } from './types'; import { getListClient } from './utils/get_list_client'; @@ -38,9 +38,9 @@ import { bulkCreateFactory, wrapHitsFactory, wrapSequencesFactory } from './fact import { RuleExecutionStatus } from '../../../../common/detection_engine/rule_monitoring'; import { truncateList } from '../rule_monitoring'; import aadFieldConversion from '../routes/index/signal_aad_mapping.json'; -import { extractReferences, injectReferences } from '../signals/saved_object_references'; +import { extractReferences, injectReferences } from './saved_object_references'; import { withSecuritySpan } from '../../../utils/with_security_span'; -import { getInputIndex, DataViewError } from '../signals/get_input_output_index'; +import { getInputIndex, DataViewError } from './utils/get_input_output_index'; import { TIMESTAMP_RUNTIME_FIELD } from './constants'; import { buildTimestampRuntimeMapping } from './utils/build_timestamp_runtime_mapping'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert_group_from_sequence.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/build_alert_group_from_sequence.test.ts similarity index 96% rename from x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert_group_from_sequence.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/build_alert_group_from_sequence.test.ts index 150c98cbee59f..98c5637b59d7a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert_group_from_sequence.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/build_alert_group_from_sequence.test.ts @@ -7,22 +7,22 @@ import { ALERT_RULE_CONSUMER } from '@kbn/rule-data-utils'; -import { sampleDocNoSortId, sampleRuleGuid } from '../../../signals/__mocks__/es_results'; +import { sampleDocNoSortId, sampleRuleGuid } from '../__mocks__/es_results'; import { buildAlertGroupFromSequence, objectArrayIntersection, objectPairIntersection, } from './build_alert_group_from_sequence'; -import { SERVER_APP_ID } from '../../../../../../common/constants'; -import { getCompleteRuleMock, getQueryRuleParams } from '../../../rule_schema/mocks'; -import type { QueryRuleParams } from '../../../rule_schema'; -import { ruleExecutionLogMock } from '../../../rule_monitoring/mocks'; +import { SERVER_APP_ID } from '../../../../../common/constants'; +import { getCompleteRuleMock, getQueryRuleParams } from '../../rule_schema/mocks'; +import type { QueryRuleParams } from '../../rule_schema'; +import { ruleExecutionLogMock } from '../../rule_monitoring/mocks'; import { ALERT_ANCESTORS, ALERT_DEPTH, ALERT_BUILDING_BLOCK_TYPE, ALERT_GROUP_ID, -} from '../../../../../../common/field_maps/field_names'; +} from '../../../../../common/field_maps/field_names'; const SPACE_ID = 'space'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert_group_from_sequence.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/build_alert_group_from_sequence.ts similarity index 89% rename from x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert_group_from_sequence.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/build_alert_group_from_sequence.ts index 369f017426315..f03d185c19996 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert_group_from_sequence.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/build_alert_group_from_sequence.ts @@ -7,26 +7,26 @@ import { ALERT_UUID } from '@kbn/rule-data-utils'; -import type { ConfigType } from '../../../../../config'; -import type { Ancestor, SignalSource, SignalSourceHit } from '../../../signals/types'; -import { buildAlert, buildAncestors, generateAlertId } from './build_alert'; -import { buildBulkBody } from './build_bulk_body'; -import type { EqlSequence } from '../../../../../../common/detection_engine/types'; -import { generateBuildingBlockIds } from './generate_building_block_ids'; -import type { BuildReasonMessage } from '../../../signals/reason_formatters'; -import type { CompleteRule, RuleParams } from '../../../rule_schema'; -import type { IRuleExecutionLogForExecutors } from '../../../rule_monitoring'; +import type { ConfigType } from '../../../../config'; +import type { Ancestor, SignalSource, SignalSourceHit } from '../types'; +import { buildAlert, buildAncestors, generateAlertId } from '../factories/utils/build_alert'; +import { buildBulkBody } from '../factories/utils/build_bulk_body'; +import type { EqlSequence } from '../../../../../common/detection_engine/types'; +import { generateBuildingBlockIds } from '../factories/utils/generate_building_block_ids'; +import type { BuildReasonMessage } from '../utils/reason_formatters'; +import type { CompleteRule, RuleParams } from '../../rule_schema'; +import type { IRuleExecutionLogForExecutors } from '../../rule_monitoring'; import { ALERT_BUILDING_BLOCK_TYPE, ALERT_GROUP_ID, ALERT_GROUP_INDEX, -} from '../../../../../../common/field_maps/field_names'; +} from '../../../../../common/field_maps/field_names'; import type { BaseFieldsLatest, EqlBuildingBlockFieldsLatest, EqlShellFieldsLatest, WrappedFieldsLatest, -} from '../../../../../../common/detection_engine/schemas/alerts'; +} from '../../../../../common/detection_engine/schemas/alerts'; /** * Takes N raw documents from ES that form a sequence and builds them into N+1 signals ready to be indexed - diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/build_eql_search_request.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/build_eql_search_request.test.ts new file mode 100644 index 0000000000000..571684f1fdcee --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/build_eql_search_request.test.ts @@ -0,0 +1,432 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; +import { getListClientMock } from '@kbn/lists-plugin/server/services/lists/list_client.mock'; +import { buildExceptionFilter } from '@kbn/lists-plugin/server/services/exception_lists'; +import { buildEqlSearchRequest } from './build_eql_search_request'; + +const emptyFilter = { + bool: { + must: [], + filter: [], + should: [], + must_not: [], + }, +}; + +describe('buildEqlSearchRequest', () => { + test('should build a basic request with time range', () => { + const request = buildEqlSearchRequest({ + query: 'process where true', + index: ['testindex1', 'testindex2'], + from: 'now-5m', + to: 'now', + size: 100, + filters: undefined, + primaryTimestamp: '@timestamp', + secondaryTimestamp: undefined, + runtimeMappings: undefined, + eventCategoryOverride: undefined, + exceptionFilter: undefined, + }); + expect(request).toEqual({ + allow_no_indices: true, + index: ['testindex1', 'testindex2'], + body: { + size: 100, + query: 'process where true', + runtime_mappings: undefined, + filter: { + bool: { + filter: [ + { + range: { + '@timestamp': { + gte: 'now-5m', + lte: 'now', + format: 'strict_date_optional_time', + }, + }, + }, + emptyFilter, + ], + }, + }, + fields: [ + { + field: '*', + include_unmapped: true, + }, + { + field: '@timestamp', + format: 'strict_date_optional_time', + }, + ], + }, + }); + }); + + test('should build a request with timestamp and event category overrides', () => { + const request = buildEqlSearchRequest({ + query: 'process where true', + index: ['testindex1', 'testindex2'], + from: 'now-5m', + to: 'now', + size: 100, + filters: undefined, + primaryTimestamp: 'event.ingested', + secondaryTimestamp: '@timestamp', + runtimeMappings: undefined, + eventCategoryOverride: 'event.other_category', + timestampField: undefined, + exceptionFilter: undefined, + }); + expect(request).toEqual({ + allow_no_indices: true, + index: ['testindex1', 'testindex2'], + body: { + event_category_field: 'event.other_category', + size: 100, + query: 'process where true', + runtime_mappings: undefined, + filter: { + bool: { + filter: [ + { + bool: { + minimum_should_match: 1, + should: [ + { + range: { + 'event.ingested': { + lte: 'now', + gte: 'now-5m', + format: 'strict_date_optional_time', + }, + }, + }, + { + bool: { + filter: [ + { + range: { + '@timestamp': { + lte: 'now', + gte: 'now-5m', + format: 'strict_date_optional_time', + }, + }, + }, + { + bool: { + must_not: { + exists: { + field: 'event.ingested', + }, + }, + }, + }, + ], + }, + }, + ], + }, + }, + emptyFilter, + ], + }, + }, + fields: [ + { + field: '*', + include_unmapped: true, + }, + { + field: 'event.ingested', + format: 'strict_date_optional_time', + }, + { + field: '@timestamp', + format: 'strict_date_optional_time', + }, + ], + }, + }); + }); + + test('should build a request without @timestamp fallback if secondaryTimestamp is not specified', () => { + const request = buildEqlSearchRequest({ + query: 'process where true', + index: ['testindex1', 'testindex2'], + from: 'now-5m', + to: 'now', + size: 100, + filters: undefined, + primaryTimestamp: 'event.ingested', + secondaryTimestamp: undefined, + runtimeMappings: undefined, + eventCategoryOverride: 'event.other_category', + timestampField: undefined, + exceptionFilter: undefined, + }); + expect(request).toEqual({ + allow_no_indices: true, + index: ['testindex1', 'testindex2'], + body: { + event_category_field: 'event.other_category', + size: 100, + query: 'process where true', + runtime_mappings: undefined, + filter: { + bool: { + filter: [ + { + range: { + 'event.ingested': { + lte: 'now', + gte: 'now-5m', + format: 'strict_date_optional_time', + }, + }, + }, + emptyFilter, + ], + }, + }, + fields: [ + { + field: '*', + include_unmapped: true, + }, + { + field: 'event.ingested', + format: 'strict_date_optional_time', + }, + ], + }, + }); + }); + + test('should build a request with exceptions', async () => { + const { filter } = await buildExceptionFilter({ + listClient: getListClientMock(), + lists: [getExceptionListItemSchemaMock()], + alias: null, + chunkSize: 1024, + excludeExceptions: true, + startedAt: new Date(), + }); + const request = buildEqlSearchRequest({ + query: 'process where true', + index: ['testindex1', 'testindex2'], + from: 'now-5m', + to: 'now', + size: 100, + filters: undefined, + primaryTimestamp: '@timestamp', + secondaryTimestamp: undefined, + runtimeMappings: undefined, + eventCategoryOverride: undefined, + exceptionFilter: filter, + }); + expect(request).toMatchInlineSnapshot(` + Object { + "allow_no_indices": true, + "body": Object { + "event_category_field": undefined, + "fields": Array [ + Object { + "field": "*", + "include_unmapped": true, + }, + Object { + "field": "@timestamp", + "format": "strict_date_optional_time", + }, + ], + "filter": Object { + "bool": Object { + "filter": Array [ + Object { + "range": Object { + "@timestamp": Object { + "format": "strict_date_optional_time", + "gte": "now-5m", + "lte": "now", + }, + }, + }, + Object { + "bool": Object { + "filter": Array [], + "must": Array [], + "must_not": Array [ + Object { + "bool": Object { + "should": Array [ + Object { + "bool": Object { + "filter": Array [ + Object { + "nested": Object { + "path": "some.parentField", + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "some.parentField.nested.field": "some value", + }, + }, + ], + }, + }, + "score_mode": "none", + }, + }, + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "some.not.nested.field": "some value", + }, + }, + ], + }, + }, + ], + }, + }, + ], + }, + }, + ], + "should": Array [], + }, + }, + ], + }, + }, + "query": "process where true", + "runtime_mappings": undefined, + "size": 100, + "tiebreaker_field": undefined, + "timestamp_field": undefined, + }, + "index": Array [ + "testindex1", + "testindex2", + ], + } + `); + }); + + test('should build a request with filters', () => { + const filters = [ + { + meta: { + alias: null, + negate: false, + disabled: false, + type: 'exists', + key: 'process.name', + value: 'exists', + }, + query: { + exists: { + field: 'process.name', + }, + }, + }, + { + meta: { + alias: null, + negate: false, + disabled: false, + type: 'phrase', + key: 'host.name', + params: { + query: 'Host-b4d9hu1a56', + }, + }, + query: { + match_phrase: { + 'host.name': 'Host-b4d9hu1a56', + }, + }, + }, + ]; + const request = buildEqlSearchRequest({ + query: 'process where true', + index: ['testindex1', 'testindex2'], + from: 'now-5m', + to: 'now', + size: 100, + filters, + primaryTimestamp: '@timestamp', + secondaryTimestamp: undefined, + runtimeMappings: undefined, + exceptionFilter: undefined, + }); + expect(request).toEqual({ + allow_no_indices: true, + index: ['testindex1', 'testindex2'], + body: { + size: 100, + query: 'process where true', + filter: { + bool: { + filter: [ + { + range: { + '@timestamp': { + gte: 'now-5m', + lte: 'now', + format: 'strict_date_optional_time', + }, + }, + }, + { + bool: { + must: [], + filter: [ + { + exists: { + field: 'process.name', + }, + }, + { + match_phrase: { + 'host.name': 'Host-b4d9hu1a56', + }, + }, + ], + should: [], + must_not: [], + }, + }, + ], + }, + }, + fields: [ + { + field: '*', + include_unmapped: true, + }, + { + field: '@timestamp', + format: 'strict_date_optional_time', + }, + ], + }, + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/build_eql_search_request.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/build_eql_search_request.ts new file mode 100644 index 0000000000000..3d8910a8e9bab --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/build_eql_search_request.ts @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { Filter } from '@kbn/es-query'; +import type { + RuleFilterArray, + TimestampOverride, +} from '../../../../../common/detection_engine/rule_schema'; +import { buildTimeRangeFilter } from '../utils/build_events_query'; +import { getQueryFilter } from '../utils/get_query_filter'; + +interface BuildEqlSearchRequestParams { + query: string; + index: string[]; + from: string; + to: string; + size: number; + filters: RuleFilterArray | undefined; + primaryTimestamp: TimestampOverride; + secondaryTimestamp: TimestampOverride | undefined; + runtimeMappings: estypes.MappingRuntimeFields | undefined; + eventCategoryOverride?: string; + timestampField?: string; + tiebreakerField?: string; + exceptionFilter: Filter | undefined; +} + +export const buildEqlSearchRequest = ({ + query, + index, + from, + to, + size, + filters, + primaryTimestamp, + secondaryTimestamp, + runtimeMappings, + eventCategoryOverride, + timestampField, + tiebreakerField, + exceptionFilter, +}: BuildEqlSearchRequestParams): estypes.EqlSearchRequest => { + const timestamps = secondaryTimestamp + ? [primaryTimestamp, secondaryTimestamp] + : [primaryTimestamp]; + const docFields = timestamps.map((tstamp) => ({ + field: tstamp, + format: 'strict_date_optional_time', + })); + + const esFilter = getQueryFilter({ + query: '', + language: 'eql', + filters: filters || [], + index, + exceptionFilter, + }); + + const rangeFilter = buildTimeRangeFilter({ + to, + from, + primaryTimestamp, + secondaryTimestamp, + }); + const requestFilter: estypes.QueryDslQueryContainer[] = [rangeFilter, esFilter]; + const fields = [ + { + field: '*', + include_unmapped: true, + }, + ...docFields, + ]; + return { + index, + allow_no_indices: true, + body: { + size, + query, + filter: { + bool: { + filter: requestFilter, + }, + }, + runtime_mappings: runtimeMappings, + timestamp_field: timestampField, + event_category_field: eventCategoryOverride, + tiebreaker_field: tiebreakerField, + fields, + }, + }; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/create_eql_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/create_eql_alert_type.ts index 6e31a9753d381..3a22dbf061ebd 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/create_eql_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/create_eql_alert_type.ts @@ -11,7 +11,7 @@ import { EQL_RULE_TYPE_ID } from '@kbn/securitysolution-rules'; import { SERVER_APP_ID } from '../../../../../common/constants'; import type { EqlRuleParams } from '../../rule_schema'; import { eqlRuleParams } from '../../rule_schema'; -import { eqlExecutor } from '../../signals/executors/eql'; +import { eqlExecutor } from './eql'; import type { CreateRuleOptions, SecurityAlertType } from '../types'; import { validateIndexPatterns } from '../utils'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/eql.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/eql.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/eql.ts similarity index 94% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/eql.ts index a67a404478b18..492e555422e09 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/eql.ts @@ -14,8 +14,8 @@ import type { } from '@kbn/alerting-plugin/server'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { Filter } from '@kbn/es-query'; -import { buildEqlSearchRequest } from '../build_events_query'; -import { createEnrichEventsFunction } from '../enrichments'; +import { buildEqlSearchRequest } from './build_eql_search_request'; +import { createEnrichEventsFunction } from '../utils/enrichments'; import type { BulkCreate, @@ -30,8 +30,8 @@ import { createSearchAfterReturnType, makeFloatString, getUnprocessedExceptionsWarnings, -} from '../utils'; -import { buildReasonMessageForEqlAlert } from '../reason_formatters'; +} from '../utils/utils'; +import { buildReasonMessageForEqlAlert } from '../utils/reason_formatters'; import type { CompleteRule, EqlRuleParams } from '../../rule_schema'; import { withSecuritySpan } from '../../../../utils/with_security_span'; import type { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/wrap_sequences_factory.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/wrap_sequences_factory.ts similarity index 91% rename from x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/wrap_sequences_factory.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/wrap_sequences_factory.ts index f20954cf9efbd..650cfb21a71c9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/wrap_sequences_factory.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/wrap_sequences_factory.ts @@ -5,8 +5,8 @@ * 2.0. */ -import type { WrapSequences } from '../../signals/types'; -import { buildAlertGroupFromSequence } from './utils/build_alert_group_from_sequence'; +import type { WrapSequences } from '../types'; +import { buildAlertGroupFromSequence } from './build_alert_group_from_sequence'; import type { ConfigType } from '../../../../config'; import type { CompleteRule, RuleParams } from '../../rule_schema'; import type { IRuleExecutionLogForExecutors } from '../../rule_monitoring'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/bulk_create_factory.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/bulk_create_factory.ts index 95bc32571f6ea..4e16c8378c03c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/bulk_create_factory.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/bulk_create_factory.ts @@ -11,7 +11,7 @@ import { isEmpty } from 'lodash'; import type { PersistenceAlertService } from '@kbn/rule-registry-plugin/server'; import type { AlertWithCommonFieldsLatest } from '@kbn/rule-registry-plugin/common/schemas'; import type { IRuleExecutionLogForExecutors } from '../../rule_monitoring'; -import { makeFloatString } from '../../signals/utils'; +import { makeFloatString } from '../utils/utils'; import type { RefreshTypes } from '../../types'; import type { BaseFieldsLatest, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/index.ts index 4d93238974487..fb2d973f11ddc 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/index.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/index.ts @@ -7,4 +7,4 @@ export * from './bulk_create_factory'; export * from './wrap_hits_factory'; -export * from './wrap_sequences_factory'; +export * from '../eql/wrap_sequences_factory'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.test.ts index 685d85d40a6b4..2b6702f591ab4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.test.ts @@ -27,9 +27,9 @@ import { } from '@kbn/rule-data-utils'; import { flattenWithPrefix } from '@kbn/securitysolution-rules'; -import { sampleDocNoSortIdWithTimestamp } from '../../../signals/__mocks__/es_results'; +import { sampleDocNoSortIdWithTimestamp } from '../../__mocks__/es_results'; import { buildAlert, buildParent, buildAncestors, additionalAlertFields } from './build_alert'; -import type { Ancestor, SignalSourceHit } from '../../../signals/types'; +import type { Ancestor, SignalSourceHit } from '../../types'; import { getListArrayMock } from '../../../../../../common/detection_engine/schemas/types/lists.mock'; import { SERVER_APP_ID } from '../../../../../../common/constants'; import { EVENT_DATASET } from '../../../../../../common/cti/constants'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts index cbc1d2cc89f96..d206fa06704f0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts @@ -43,13 +43,14 @@ import { flattenWithPrefix } from '@kbn/securitysolution-rules'; import { createHash } from 'crypto'; -import type { BaseSignalHit, SimpleHit, ThresholdResult } from '../../../signals/types'; +import type { BaseSignalHit, SimpleHit } from '../../types'; +import type { ThresholdResult } from '../../threshold/types'; import { getField, getValidDateFromDoc, isWrappedDetectionAlert, isWrappedSignalHit, -} from '../../../signals/utils'; +} from '../../utils/utils'; import { SERVER_APP_ID } from '../../../../../../common/constants'; import type { SearchTypes } from '../../../../telemetry/types'; import { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_bulk_body.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_bulk_body.ts index 5a44a4d06260a..cd7351362db0b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_bulk_body.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_bulk_body.ts @@ -10,16 +10,16 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/types'; import type { BaseHit, SearchTypes } from '../../../../../../common/detection_engine/types'; import type { ConfigType } from '../../../../../config'; -import type { BuildReasonMessage } from '../../../signals/reason_formatters'; -import { getMergeStrategy } from '../../../signals/source_fields_merging/strategies'; -import type { BaseSignalHit, SignalSource, SignalSourceHit } from '../../../signals/types'; +import type { BuildReasonMessage } from '../../utils/reason_formatters'; +import { getMergeStrategy } from '../../utils/source_fields_merging/strategies'; +import type { BaseSignalHit, SignalSource, SignalSourceHit } from '../../types'; import { additionalAlertFields, buildAlert } from './build_alert'; import { filterSource } from './filter_source'; import type { CompleteRule, RuleParams } from '../../../rule_schema'; import type { IRuleExecutionLogForExecutors } from '../../../rule_monitoring'; -import { buildRuleNameFromMapping } from '../../../signals/mappings/build_rule_name_from_mapping'; -import { buildSeverityFromMapping } from '../../../signals/mappings/build_severity_from_mapping'; -import { buildRiskScoreFromMapping } from '../../../signals/mappings/build_risk_score_from_mapping'; +import { buildRuleNameFromMapping } from '../../utils/mappings/build_rule_name_from_mapping'; +import { buildSeverityFromMapping } from '../../utils/mappings/build_severity_from_mapping'; +import { buildRiskScoreFromMapping } from '../../utils/mappings/build_risk_score_from_mapping'; import type { BaseFieldsLatest } from '../../../../../../common/detection_engine/schemas/alerts'; import { stripNonEcsFields } from './strip_non_ecs_fields'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/filter_source.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/filter_source.ts index 58e99969e74a8..c0172207c7a60 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/filter_source.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/filter_source.ts @@ -6,7 +6,7 @@ */ import { ALERT_THRESHOLD_RESULT } from '../../../../../../common/field_maps/field_names'; -import type { SignalSourceHit } from '../../../signals/types'; +import type { SignalSourceHit } from '../../types'; export const filterSource = (doc: SignalSourceHit) => { const docSource = doc._source ?? {}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/wrap_hits_factory.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/wrap_hits_factory.ts index ee49980bffac9..aae7501bf3798 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/wrap_hits_factory.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/wrap_hits_factory.ts @@ -9,11 +9,11 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { ALERT_UUID } from '@kbn/rule-data-utils'; import type { ConfigType } from '../../../../config'; -import type { SignalSource, SimpleHit } from '../../signals/types'; +import type { SignalSource, SimpleHit } from '../types'; import type { CompleteRule, RuleParams } from '../../rule_schema'; -import { generateId } from '../../signals/utils'; +import { generateId } from '../utils/utils'; import { buildBulkBody } from './utils/build_bulk_body'; -import type { BuildReasonMessage } from '../../signals/reason_formatters'; +import type { BuildReasonMessage } from '../utils/reason_formatters'; import type { BaseFieldsLatest, WrappedFieldsLatest, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts index 796900829d6ea..d85aa18e3b71f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts @@ -11,7 +11,7 @@ import { SERVER_APP_ID } from '../../../../../common/constants'; import type { ThreatRuleParams } from '../../rule_schema'; import { threatRuleParams } from '../../rule_schema'; -import { threatMatchExecutor } from '../../signals/executors/threat_match'; +import { indicatorMatchExecutor } from './indicator_match'; import type { CreateRuleOptions, SecurityAlertType } from '../types'; import { validateIndexPatterns } from '../utils'; @@ -82,7 +82,7 @@ export const createIndicatorMatchAlertType = ( state, } = execOptions; - const result = await threatMatchExecutor({ + const result = await indicatorMatchExecutor({ inputIndex, runtimeMappings, completeRule, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threat_match.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/indicator_match.ts similarity index 94% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threat_match.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/indicator_match.ts index 8ebf39867dea4..15f578efbc2bd 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threat_match.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/indicator_match.ts @@ -17,13 +17,13 @@ import type { ListClient } from '@kbn/lists-plugin/server'; import type { Filter } from '@kbn/es-query'; import type { RuleRangeTuple, BulkCreate, WrapHits } from '../types'; import type { ITelemetryEventsSender } from '../../../telemetry/sender'; -import { createThreatSignals } from '../threat_mapping/create_threat_signals'; +import { createThreatSignals } from './threat_mapping/create_threat_signals'; import type { CompleteRule, ThreatRuleParams } from '../../rule_schema'; import { withSecuritySpan } from '../../../../utils/with_security_span'; import { DEFAULT_INDICATOR_SOURCE_PATH } from '../../../../../common/constants'; import type { IRuleExecutionLogForExecutors } from '../../rule_monitoring'; -export const threatMatchExecutor = async ({ +export const indicatorMatchExecutor = async ({ inputIndex, runtimeMappings, completeRule, @@ -60,7 +60,7 @@ export const threatMatchExecutor = async ({ }) => { const ruleParams = completeRule.ruleParams; - return withSecuritySpan('threatMatchExecutor', async () => { + return withSecuritySpan('indicatorMatchExecutor', async () => { return createThreatSignals({ alertId: completeRule.alertId, bulkCreate, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/build_threat_enrichment.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/build_threat_enrichment.ts similarity index 97% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/build_threat_enrichment.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/build_threat_enrichment.ts index 18cf4240d0b18..70819f6896861 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/build_threat_enrichment.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/build_threat_enrichment.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { SignalsEnrichment } from '../types'; +import type { SignalsEnrichment } from '../../types'; import type { BuildThreatEnrichmentOptions } from './types'; import { buildThreatMappingFilter } from './build_threat_mapping_filter'; import { getSignalsQueryMapFromThreatIndex } from './get_signals_map_from_threat_index'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/build_threat_mapping_filter.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/build_threat_mapping_filter.mock.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/build_threat_mapping_filter.mock.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/build_threat_mapping_filter.mock.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/build_threat_mapping_filter.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/build_threat_mapping_filter.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/build_threat_mapping_filter.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/build_threat_mapping_filter.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/build_threat_mapping_filter.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/build_threat_mapping_filter.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/build_threat_mapping_filter.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/build_threat_mapping_filter.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_event_signal.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/create_event_signal.ts similarity index 93% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_event_signal.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/create_event_signal.ts index c006ab528b3c5..8fea8412be38a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_event_signal.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/create_event_signal.ts @@ -6,11 +6,11 @@ */ import { buildThreatMappingFilter } from './build_threat_mapping_filter'; -import { getFilter } from '../get_filter'; -import { searchAfterAndBulkCreate } from '../search_after_bulk_create'; -import { buildReasonMessageForThreatMatchAlert } from '../reason_formatters'; +import { getFilter } from '../../utils/get_filter'; +import { searchAfterAndBulkCreate } from '../../utils/search_after_bulk_create'; +import { buildReasonMessageForThreatMatchAlert } from '../../utils/reason_formatters'; import type { CreateEventSignalOptions } from './types'; -import type { SearchAfterAndBulkCreateReturnType } from '../types'; +import type { SearchAfterAndBulkCreateReturnType } from '../../types'; import { getSignalsQueryMapFromThreatIndex } from './get_signals_map_from_threat_index'; import { threatEnrichmentFactory } from './threat_enrichment_factory'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signal.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/create_threat_signal.ts similarity index 91% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signal.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/create_threat_signal.ts index 8773abbdae842..abaac2456cd9b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signal.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/create_threat_signal.ts @@ -6,11 +6,11 @@ */ import { buildThreatMappingFilter } from './build_threat_mapping_filter'; -import { getFilter } from '../get_filter'; -import { searchAfterAndBulkCreate } from '../search_after_bulk_create'; -import { buildReasonMessageForThreatMatchAlert } from '../reason_formatters'; +import { getFilter } from '../../utils/get_filter'; +import { searchAfterAndBulkCreate } from '../../utils/search_after_bulk_create'; +import { buildReasonMessageForThreatMatchAlert } from '../../utils/reason_formatters'; import type { CreateThreatSignalOptions } from './types'; -import type { SearchAfterAndBulkCreateReturnType } from '../types'; +import type { SearchAfterAndBulkCreateReturnType } from '../../types'; import { buildThreatEnrichment } from './build_threat_enrichment'; export const createThreatSignal = async ({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/create_threat_signals.ts similarity index 98% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/create_threat_signals.ts index a73485a4fd375..acfe38eaca34a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/create_threat_signals.ts @@ -16,7 +16,7 @@ import type { } from './types'; import { createThreatSignal } from './create_threat_signal'; import { createEventSignal } from './create_event_signal'; -import type { SearchAfterAndBulkCreateReturnType } from '../types'; +import type { SearchAfterAndBulkCreateReturnType } from '../../types'; import { buildExecutionIntervalValidator, combineConcurrentResults, @@ -26,7 +26,7 @@ import { getAllowedFieldsForTermQuery } from './get_allowed_fields_for_terms_que import { getEventCount, getEventList } from './get_event_count'; import { getMappingFilters } from './get_mapping_filters'; -import { THREAT_PIT_KEEP_ALIVE } from '../../../../../common/cti/constants'; +import { THREAT_PIT_KEEP_ALIVE } from '../../../../../../common/cti/constants'; export const createThreatSignals = async ({ alertId, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/enrich_signal_threat_matches.mock.ts similarity index 93% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.mock.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/enrich_signal_threat_matches.mock.ts index ef68285669de2..9546fabf70915 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/enrich_signal_threat_matches.mock.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { SignalSourceHit } from '../types'; +import type { SignalSourceHit } from '../../types'; import type { ThreatMatchNamedQuery } from './types'; export const getNamedQueryMock = ( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/enrich_signal_threat_matches.test.ts similarity index 99% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/enrich_signal_threat_matches.test.ts index 2658c90720adb..c9e2d7ec7b275 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/enrich_signal_threat_matches.test.ts @@ -6,9 +6,9 @@ */ import { get } from 'lodash'; -import { ENRICHMENT_DESTINATION_PATH } from '../../../../../common/constants'; -import { ENRICHMENT_TYPES } from '../../../../../common/cti/constants'; -import type { SignalSourceHit } from '../types'; +import { ENRICHMENT_DESTINATION_PATH } from '../../../../../../common/constants'; +import { ENRICHMENT_TYPES } from '../../../../../../common/cti/constants'; +import type { SignalSourceHit } from '../../types'; import { getThreatListItemMock } from './build_threat_mapping_filter.mock'; import { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/enrich_signal_threat_matches.ts similarity index 98% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/enrich_signal_threat_matches.ts index 28f43519f118f..303b7fa9eebfe 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/enrich_signal_threat_matches.ts @@ -6,9 +6,9 @@ */ import { get, isObject } from 'lodash'; -import { ENRICHMENT_TYPES, FEED_NAME_PATH } from '../../../../../common/cti/constants'; +import { ENRICHMENT_TYPES, FEED_NAME_PATH } from '../../../../../../common/cti/constants'; -import type { SignalSourceHit } from '../types'; +import type { SignalSourceHit } from '../../types'; import type { ThreatEnrichment, ThreatListItem, ThreatMatchNamedQuery } from './types'; export const MAX_NUMBER_OF_SIGNAL_MATCHES = 200; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_allowed_fields_for_terms_query.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/get_allowed_fields_for_terms_query.test.ts similarity index 98% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_allowed_fields_for_terms_query.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/get_allowed_fields_for_terms_query.test.ts index b13b5d23278ba..deb182e6860dd 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_allowed_fields_for_terms_query.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/get_allowed_fields_for_terms_query.test.ts @@ -7,7 +7,7 @@ import type { IndicesGetFieldMappingResponse } from '@elastic/elasticsearch/lib/api/types'; import type { RuleExecutorServicesMock } from '@kbn/alerting-plugin/server/mocks'; import { alertsMock } from '@kbn/alerting-plugin/server/mocks'; -import { ruleExecutionLogMock } from '../../rule_monitoring/mocks'; +import { ruleExecutionLogMock } from '../../../rule_monitoring/mocks'; import { getAllowedFieldForTermQueryFromMapping, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_allowed_fields_for_terms_query.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/get_allowed_fields_for_terms_query.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_allowed_fields_for_terms_query.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/get_allowed_fields_for_terms_query.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_event_count.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/get_event_count.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_event_count.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/get_event_count.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_event_count.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/get_event_count.ts similarity index 92% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_event_count.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/get_event_count.ts index c17696fbddf20..a48eae47daa9a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_event_count.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/get_event_count.ts @@ -7,9 +7,9 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { EventCountOptions, EventsOptions, EventDoc } from './types'; -import { getQueryFilter } from '../get_query_filter'; -import { singleSearchAfter } from '../single_search_after'; -import { buildEventsSearchQuery } from '../build_events_query'; +import { getQueryFilter } from '../../utils/get_query_filter'; +import { singleSearchAfter } from '../../utils/single_search_after'; +import { buildEventsSearchQuery } from '../../utils/build_events_query'; export const MAX_PER_PAGE = 9000; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_mapping_filters.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/get_mapping_filters.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_mapping_filters.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/get_mapping_filters.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_mapping_filters.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/get_mapping_filters.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_mapping_filters.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/get_mapping_filters.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_signals_map_from_threat_index.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/get_signals_map_from_threat_index.mock.ts similarity index 93% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_signals_map_from_threat_index.mock.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/get_signals_map_from_threat_index.mock.ts index 9fa23d25f2899..41b479b55d064 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_signals_map_from_threat_index.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/get_signals_map_from_threat_index.mock.ts @@ -8,7 +8,7 @@ import { elasticsearchServiceMock } from '@kbn/core/server/mocks'; import type { GetThreatListOptions } from './types'; import { getListClientMock } from '@kbn/lists-plugin/server/services/lists/list_client.mock'; -import { ruleExecutionLogMock } from '../../rule_monitoring/mocks'; +import { ruleExecutionLogMock } from '../../../rule_monitoring/mocks'; const esClient = elasticsearchServiceMock.createElasticsearchClient(); const ruleExecutionLogger = ruleExecutionLogMock.forExecutors.create(); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_signals_map_from_threat_index.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/get_signals_map_from_threat_index.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_signals_map_from_threat_index.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/get_signals_map_from_threat_index.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_signals_map_from_threat_index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/get_signals_map_from_threat_index.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_signals_map_from_threat_index.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/get_signals_map_from_threat_index.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_threat_list.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/get_threat_list.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_threat_list.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/get_threat_list.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_threat_list.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/get_threat_list.ts similarity index 97% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_threat_list.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/get_threat_list.ts index 160de278f47e3..50bcf3c63cb0f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_threat_list.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/get_threat_list.ts @@ -6,7 +6,7 @@ */ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { getQueryFilter } from '../get_query_filter'; +import { getQueryFilter } from '../../utils/get_query_filter'; import type { GetThreatListOptions, ThreatListCountOptions, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/threat_enrichment_factory.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/threat_enrichment_factory.test.ts similarity index 98% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/threat_enrichment_factory.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/threat_enrichment_factory.test.ts index 3c6bb7d8ee169..ddee331060a9a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/threat_enrichment_factory.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/threat_enrichment_factory.test.ts @@ -7,7 +7,7 @@ import { getThreatList } from './get_threat_list'; import { getNamedQueryMock } from './enrich_signal_threat_matches.mock'; -import type { SignalSourceHit } from '../types'; +import type { SignalSourceHit } from '../../types'; import { threatSearchParamsMock } from './get_signals_map_from_threat_index.mock'; import { threatEnrichmentFactory } from './threat_enrichment_factory'; import { enrichSignalThreatMatchesFromSignalsMap } from './enrich_signal_threat_matches'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/threat_enrichment_factory.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/threat_enrichment_factory.ts similarity index 97% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/threat_enrichment_factory.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/threat_enrichment_factory.ts index 9d8da19f613bb..1bf61512135e0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/threat_enrichment_factory.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/threat_enrichment_factory.ts @@ -6,7 +6,7 @@ */ import type { CreateEventSignalOptions, GetThreatListOptions } from './types'; -import type { SignalSourceHit } from '../types'; +import type { SignalSourceHit } from '../../types'; import { getThreatList } from './get_threat_list'; import { enrichSignalThreatMatchesFromSignalsMap } from './enrich_signal_threat_matches'; import { type SignalsQueryMap } from './get_signals_map_from_threat_index'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/types.ts similarity index 97% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/types.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/types.ts index 63cf588ae43a1..243f67a8d773e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/types.ts @@ -28,16 +28,16 @@ import type { } from '@kbn/alerting-plugin/server'; import type { ElasticsearchClient } from '@kbn/core/server'; import type { Filter } from '@kbn/es-query'; -import type { ITelemetryEventsSender } from '../../../telemetry/sender'; +import type { ITelemetryEventsSender } from '../../../../telemetry/sender'; import type { BulkCreate, RuleRangeTuple, SearchAfterAndBulkCreateReturnType, WrapHits, OverrideBodyQuery, -} from '../types'; -import type { CompleteRule, ThreatRuleParams } from '../../rule_schema'; -import type { IRuleExecutionLogForExecutors } from '../../rule_monitoring'; +} from '../../types'; +import type { CompleteRule, ThreatRuleParams } from '../../../rule_schema'; +import type { IRuleExecutionLogForExecutors } from '../../../rule_monitoring'; export type SortOrderOrUndefined = 'asc' | 'desc' | undefined; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/utils.test.ts similarity index 99% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/utils.test.ts index 57eae75ba4f5c..7554222960203 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/utils.test.ts @@ -5,8 +5,8 @@ * 2.0. */ -import type { SearchAfterAndBulkCreateReturnType } from '../types'; -import { sampleSignalHit } from '../__mocks__/es_results'; +import type { SearchAfterAndBulkCreateReturnType } from '../../types'; +import { sampleSignalHit } from '../../__mocks__/es_results'; import type { ThreatMatchNamedQuery, ThreatTermNamedQuery } from './types'; import { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/utils.ts similarity index 99% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/utils.ts index 4e97fd36033d5..dd90e52cddd01 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/utils.ts @@ -9,8 +9,8 @@ import moment from 'moment'; import type { ThreatMapping } from '@kbn/securitysolution-io-ts-alerting-types'; import { get } from 'lodash'; -import type { SearchAfterAndBulkCreateReturnType, SignalSourceHit } from '../types'; -import { parseInterval } from '../utils'; +import type { SearchAfterAndBulkCreateReturnType, SignalSourceHit } from '../../types'; +import { parseInterval } from '../../utils/utils'; import { ThreatMatchQueryType } from './types'; import type { ThreatListItem, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_ml_signals.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/bulk_create_ml_signals.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_ml_signals.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/bulk_create_ml_signals.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_ml_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/bulk_create_ml_signals.ts similarity index 84% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_ml_signals.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/bulk_create_ml_signals.ts index 7f565036abe3f..25ff5cca03f2e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_ml_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/bulk_create_ml_signals.ts @@ -13,14 +13,14 @@ import type { AlertInstanceState, RuleExecutorServices, } from '@kbn/alerting-plugin/server'; -import type { GenericBulkCreateResponse } from '../rule_types/factories'; -import type { Anomaly } from '../../machine_learning'; -import type { BulkCreate, WrapHits } from './types'; -import type { CompleteRule, MachineLearningRuleParams } from '../rule_schema'; -import { buildReasonMessageForMlAlert } from './reason_formatters'; -import type { BaseFieldsLatest } from '../../../../common/detection_engine/schemas/alerts'; -import type { IRuleExecutionLogForExecutors } from '../rule_monitoring'; -import { createEnrichEventsFunction } from './enrichments'; +import type { GenericBulkCreateResponse } from '../factories'; +import type { Anomaly } from '../../../machine_learning'; +import type { BulkCreate, WrapHits } from '../types'; +import type { CompleteRule, MachineLearningRuleParams } from '../../rule_schema'; +import { buildReasonMessageForMlAlert } from '../utils/reason_formatters'; +import type { BaseFieldsLatest } from '../../../../../common/detection_engine/schemas/alerts'; +import type { IRuleExecutionLogForExecutors } from '../../rule_monitoring'; +import { createEnrichEventsFunction } from '../utils/enrichments'; interface BulkCreateMlSignalsParams { anomalyHits: Array>; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/create_ml_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/create_ml_alert_type.ts index c652de348484f..753e837f08b5f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/create_ml_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/create_ml_alert_type.ts @@ -11,7 +11,7 @@ import { SERVER_APP_ID } from '../../../../../common/constants'; import type { MachineLearningRuleParams } from '../../rule_schema'; import { machineLearningRuleParams } from '../../rule_schema'; -import { mlExecutor } from '../../signals/executors/ml'; +import { mlExecutor } from './ml'; import type { CreateRuleOptions, SecurityAlertType } from '../types'; export const createMlAlertType = ( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/find_ml_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/find_ml_signals.ts similarity index 90% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/find_ml_signals.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/find_ml_signals.ts index fa66548e20503..fdb7317e85bfa 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/find_ml_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/find_ml_signals.ts @@ -9,8 +9,8 @@ import dateMath from '@kbn/datemath'; import type { KibanaRequest, SavedObjectsClientContract } from '@kbn/core/server'; import type { MlPluginSetup } from '@kbn/ml-plugin/server'; import type { Filter } from '@kbn/es-query'; -import type { AnomalyResults } from '../../machine_learning'; -import { getAnomalies } from '../../machine_learning'; +import type { AnomalyResults } from '../../../machine_learning'; +import { getAnomalies } from '../../../machine_learning'; export const findMlSignals = async ({ ml, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/ml.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/ml.test.ts similarity index 95% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/ml.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/ml.test.ts index 042073005e4a1..d1adcc535c1cc 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/ml.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/ml.test.ts @@ -11,14 +11,14 @@ import { alertsMock } from '@kbn/alerting-plugin/server/mocks'; import { mlExecutor } from './ml'; import { getCompleteRuleMock, getMlRuleParams } from '../../rule_schema/mocks'; import { getListClientMock } from '@kbn/lists-plugin/server/services/lists/list_client.mock'; -import { findMlSignals } from '../find_ml_signals'; -import { bulkCreateMlSignals } from '../bulk_create_ml_signals'; +import { findMlSignals } from './find_ml_signals'; +import { bulkCreateMlSignals } from './bulk_create_ml_signals'; import { mlPluginServerMock } from '@kbn/ml-plugin/server/mocks'; import type { MachineLearningRuleParams } from '../../rule_schema'; import { ruleExecutionLogMock } from '../../rule_monitoring/mocks'; -jest.mock('../find_ml_signals'); -jest.mock('../bulk_create_ml_signals'); +jest.mock('./find_ml_signals'); +jest.mock('./bulk_create_ml_signals'); describe('ml_executor', () => { let jobsSummaryMock: jest.Mock; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/ml.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/ml.ts similarity index 95% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/ml.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/ml.ts index 9cd7a6bce13dc..c0fb5401bede8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/ml.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/ml.ts @@ -16,16 +16,16 @@ import type { ListClient } from '@kbn/lists-plugin/server'; import type { Filter } from '@kbn/es-query'; import { isJobStarted } from '../../../../../common/machine_learning/helpers'; import type { CompleteRule, MachineLearningRuleParams } from '../../rule_schema'; -import { bulkCreateMlSignals } from '../bulk_create_ml_signals'; -import { filterEventsAgainstList } from '../filters/filter_events_against_list'; -import { findMlSignals } from '../find_ml_signals'; +import { bulkCreateMlSignals } from './bulk_create_ml_signals'; +import { filterEventsAgainstList } from '../utils/large_list_filters/filter_events_against_list'; +import { findMlSignals } from './find_ml_signals'; import type { BulkCreate, RuleRangeTuple, WrapHits } from '../types'; import { addToSearchAfterReturn, createErrorsFromShard, createSearchAfterReturnType, mergeReturns, -} from '../utils'; +} from '../utils/utils'; import type { SetupPlugins } from '../../../../plugin'; import { withSecuritySpan } from '../../../../utils/with_security_span'; import type { IRuleExecutionLogForExecutors } from '../../rule_monitoring'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/build_new_terms_aggregation.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/build_new_terms_aggregation.ts index e9bf89554941f..93ba7064c6ce3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/build_new_terms_aggregation.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/build_new_terms_aggregation.ts @@ -7,7 +7,7 @@ import type { Moment } from 'moment'; import type { ESSearchResponse } from '@kbn/es-types'; -import type { SignalSource } from '../../signals/types'; +import type { SignalSource } from '../types'; export type RecentTermsAggResult = ESSearchResponse< SignalSource, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/create_new_terms_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/create_new_terms_alert_type.ts index 64a1f4b093d42..c4525de1b00b3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/create_new_terms_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/create_new_terms_alert_type.ts @@ -12,10 +12,10 @@ import { SERVER_APP_ID } from '../../../../../common/constants'; import type { NewTermsRuleParams } from '../../rule_schema'; import { newTermsRuleParams } from '../../rule_schema'; import type { CreateRuleOptions, SecurityAlertType } from '../types'; -import { singleSearchAfter } from '../../signals/single_search_after'; -import { getFilter } from '../../signals/get_filter'; -import { wrapNewTermsAlerts } from '../factories/utils/wrap_new_terms_alerts'; -import type { EventsAndTerms } from '../factories/utils/wrap_new_terms_alerts'; +import { singleSearchAfter } from '../utils/single_search_after'; +import { getFilter } from '../utils/get_filter'; +import { wrapNewTermsAlerts } from './wrap_new_terms_alerts'; +import type { EventsAndTerms } from './wrap_new_terms_alerts'; import type { DocFetchAggResult, RecentTermsAggResult, @@ -39,8 +39,8 @@ import { addToSearchAfterReturn, createSearchAfterReturnType, getUnprocessedExceptionsWarnings, -} from '../../signals/utils'; -import { createEnrichEventsFunction } from '../../signals/enrichments'; +} from '../utils/utils'; +import { createEnrichEventsFunction } from '../utils/enrichments'; export const createNewTermsAlertType = ( createOptions: CreateRuleOptions diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/wrap_new_terms_alerts.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/wrap_new_terms_alerts.test.ts similarity index 93% rename from x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/wrap_new_terms_alerts.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/wrap_new_terms_alerts.test.ts index 3060deaf712bd..69d0b90b45c29 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/wrap_new_terms_alerts.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/wrap_new_terms_alerts.test.ts @@ -6,10 +6,10 @@ */ import { ALERT_UUID } from '@kbn/rule-data-utils'; -import { ALERT_NEW_TERMS } from '../../../../../../common/field_maps/field_names'; -import { getCompleteRuleMock, getNewTermsRuleParams } from '../../../rule_schema/mocks'; -import { ruleExecutionLogMock } from '../../../rule_monitoring/mocks'; -import { sampleDocNoSortIdWithTimestamp } from '../../../signals/__mocks__/es_results'; +import { ALERT_NEW_TERMS } from '../../../../../common/field_maps/field_names'; +import { getCompleteRuleMock, getNewTermsRuleParams } from '../../rule_schema/mocks'; +import { ruleExecutionLogMock } from '../../rule_monitoring/mocks'; +import { sampleDocNoSortIdWithTimestamp } from '../__mocks__/es_results'; import { wrapNewTermsAlerts } from './wrap_new_terms_alerts'; const ruleExecutionLogger = ruleExecutionLogMock.forExecutors.create(); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/wrap_new_terms_alerts.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/wrap_new_terms_alerts.ts similarity index 77% rename from x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/wrap_new_terms_alerts.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/wrap_new_terms_alerts.ts index d87bdceb9cecd..424c52273c30a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/wrap_new_terms_alerts.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/wrap_new_terms_alerts.ts @@ -12,14 +12,14 @@ import type { BaseFieldsLatest, NewTermsFieldsLatest, WrappedFieldsLatest, -} from '../../../../../../common/detection_engine/schemas/alerts'; -import { ALERT_NEW_TERMS } from '../../../../../../common/field_maps/field_names'; -import type { ConfigType } from '../../../../../config'; -import type { CompleteRule, RuleParams } from '../../../rule_schema'; -import { buildReasonMessageForNewTermsAlert } from '../../../signals/reason_formatters'; -import type { SignalSource } from '../../../signals/types'; -import type { IRuleExecutionLogForExecutors } from '../../../rule_monitoring'; -import { buildBulkBody } from './build_bulk_body'; +} from '../../../../../common/detection_engine/schemas/alerts'; +import { ALERT_NEW_TERMS } from '../../../../../common/field_maps/field_names'; +import type { ConfigType } from '../../../../config'; +import type { CompleteRule, RuleParams } from '../../rule_schema'; +import { buildReasonMessageForNewTermsAlert } from '../utils/reason_formatters'; +import type { SignalSource } from '../types'; +import type { IRuleExecutionLogForExecutors } from '../../rule_monitoring'; +import { buildBulkBody } from '../factories/utils/build_bulk_body'; export interface EventsAndTerms { event: estypes.SearchHit; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/alert_suppression/__snapshots__/build_group_by_field_aggregation.test.ts.snap b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/__snapshots__/build_group_by_field_aggregation.test.ts.snap similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/alert_suppression/__snapshots__/build_group_by_field_aggregation.test.ts.snap rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/__snapshots__/build_group_by_field_aggregation.test.ts.snap diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/alert_suppression/__snapshots__/group_and_bulk_create.test.ts.snap b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/__snapshots__/group_and_bulk_create.test.ts.snap similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/alert_suppression/__snapshots__/group_and_bulk_create.test.ts.snap rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/__snapshots__/group_and_bulk_create.test.ts.snap diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/alert_suppression/build_group_by_field_aggregation.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/build_group_by_field_aggregation.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/alert_suppression/build_group_by_field_aggregation.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/build_group_by_field_aggregation.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/alert_suppression/build_group_by_field_aggregation.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/build_group_by_field_aggregation.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/alert_suppression/build_group_by_field_aggregation.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/build_group_by_field_aggregation.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/bulk_create_with_suppression.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/bulk_create_with_suppression.ts similarity index 91% rename from x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/bulk_create_with_suppression.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/bulk_create_with_suppression.ts index fb49a498805f9..88af364c732a4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/bulk_create_with_suppression.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/bulk_create_with_suppression.ts @@ -13,14 +13,14 @@ import type { AlertWithCommonFieldsLatest, SuppressionFieldsLatest, } from '@kbn/rule-registry-plugin/common/schemas'; -import type { IRuleExecutionLogForExecutors } from '../../rule_monitoring'; -import { makeFloatString } from '../../signals/utils'; +import type { IRuleExecutionLogForExecutors } from '../../../rule_monitoring'; +import { makeFloatString } from '../../utils/utils'; import type { BaseFieldsLatest, WrappedFieldsLatest, -} from '../../../../../common/detection_engine/schemas/alerts'; -import type { RuleServices } from '../../signals/types'; -import { createEnrichEventsFunction } from '../../signals/enrichments'; +} from '../../../../../../common/detection_engine/schemas/alerts'; +import type { RuleServices } from '../../types'; +import { createEnrichEventsFunction } from '../../utils/enrichments'; export interface GenericBulkCreateResponse { success: boolean; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/alert_suppression/group_and_bulk_create.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/group_and_bulk_create.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/alert_suppression/group_and_bulk_create.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/group_and_bulk_create.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/alert_suppression/group_and_bulk_create.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/group_and_bulk_create.ts similarity index 83% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/alert_suppression/group_and_bulk_create.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/group_and_bulk_create.ts index 5e67b4c01f6ce..99f1901aad9f8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/alert_suppression/group_and_bulk_create.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/group_and_bulk_create.ts @@ -9,25 +9,55 @@ import type moment from 'moment'; import type * as estypes from '@elastic/elasticsearch/lib/api/types'; -import { withSecuritySpan } from '../../../../utils/with_security_span'; -import { buildTimeRangeFilter } from '../build_events_query'; +import type { ESSearchResponse } from '@kbn/es-types'; + +import { withSecuritySpan } from '../../../../../utils/with_security_span'; +import { buildTimeRangeFilter } from '../../utils/build_events_query'; import type { - EventGroupingMultiBucketAggregationResult, - GroupAndBulkCreateParams, - GroupAndBulkCreateReturnType, -} from '../types'; -import { addToSearchAfterReturn, getUnprocessedExceptionsWarnings } from '../utils'; -import type { SuppressionBuckets } from '../../rule_types/factories/utils/wrap_suppressed_alerts'; -import { wrapSuppressedAlerts } from '../../rule_types/factories/utils/wrap_suppressed_alerts'; + RuleServices, + RunOpts, + SearchAfterAndBulkCreateReturnType, + SignalSource, +} from '../../types'; +import { addToSearchAfterReturn, getUnprocessedExceptionsWarnings } from '../../utils/utils'; +import type { SuppressionBuckets } from './wrap_suppressed_alerts'; +import { wrapSuppressedAlerts } from './wrap_suppressed_alerts'; import { buildGroupByFieldAggregation } from './build_group_by_field_aggregation'; -import { singleSearchAfter } from '../single_search_after'; -import { bulkCreateWithSuppression } from '../../rule_types/factories/bulk_create_with_suppression'; +import { singleSearchAfter } from '../../utils/single_search_after'; +import { bulkCreateWithSuppression } from './bulk_create_with_suppression'; +import type { UnifiedQueryRuleParams } from '../../../rule_schema'; +import type { BuildReasonMessage } from '../../utils/reason_formatters'; export interface BucketHistory { key: Record; endDate: string; } +export interface GroupAndBulkCreateParams { + runOpts: RunOpts; + services: RuleServices; + spaceId: string; + filter: estypes.QueryDslQueryContainer; + buildReasonMessage: BuildReasonMessage; + bucketHistory?: BucketHistory[]; + groupByFields: string[]; +} + +export interface GroupAndBulkCreateReturnType extends SearchAfterAndBulkCreateReturnType { + state: { + suppressionGroupHistory: BucketHistory[]; + }; +} + +type EventGroupingMultiBucketAggregationResult = ESSearchResponse< + SignalSource, + { + body: { + aggregations: ReturnType; + }; + } +>; + /** * Builds a filter that excludes documents from existing buckets. */ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/wrap_suppressed_alerts.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/wrap_suppressed_alerts.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/wrap_suppressed_alerts.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/wrap_suppressed_alerts.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/wrap_suppressed_alerts.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/wrap_suppressed_alerts.ts similarity index 94% rename from x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/wrap_suppressed_alerts.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/wrap_suppressed_alerts.ts index 581d6a256723e..42fe0954a1847 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/wrap_suppressed_alerts.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/wrap_suppressed_alerts.ts @@ -23,9 +23,9 @@ import type { import type { ConfigType } from '../../../../../config'; import type { CompleteRule, RuleParams } from '../../../rule_schema'; import type { IRuleExecutionLogForExecutors } from '../../../rule_monitoring'; -import type { SignalSource } from '../../../signals/types'; -import { buildBulkBody } from './build_bulk_body'; -import type { BuildReasonMessage } from '../../../signals/reason_formatters'; +import type { SignalSource } from '../../types'; +import { buildBulkBody } from '../../factories/utils/build_bulk_body'; +import type { BuildReasonMessage } from '../../utils/reason_formatters'; export interface SuppressionBuckets { event: estypes.SearchHit; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts index 0478c4d49c1ba..4d6b3120488fa 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts @@ -14,13 +14,13 @@ import { createSecurityRuleTypeWrapper } from '../create_security_rule_type_wrap import { createMockConfig } from '../../routes/__mocks__'; import { createMockTelemetryEventsSender } from '../../../telemetry/__mocks__'; import { ruleExecutionLogMock } from '../../rule_monitoring/mocks'; -import { sampleDocNoSortId } from '../../signals/__mocks__/es_results'; +import { sampleDocNoSortId } from '../__mocks__/es_results'; import { getQueryRuleParams } from '../../rule_schema/mocks'; import { licensingMock } from '@kbn/licensing-plugin/server/mocks'; import { QUERY_RULE_TYPE_ID } from '@kbn/securitysolution-rules'; -jest.mock('../../signals/utils', () => ({ - ...jest.requireActual('../../signals/utils'), +jest.mock('../utils/utils', () => ({ + ...jest.requireActual('../utils/utils'), getExceptions: () => [], })); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts index 89a43f896a4d9..2aa3a708c672a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts @@ -8,10 +8,10 @@ import { validateNonExact } from '@kbn/securitysolution-io-ts-utils'; import { SERVER_APP_ID } from '../../../../../common/constants'; -import type { BucketHistory } from '../../signals/alert_suppression/group_and_bulk_create'; +import type { BucketHistory } from './alert_suppression/group_and_bulk_create'; import type { UnifiedQueryRuleParams } from '../../rule_schema'; import { unifiedQueryRuleParams } from '../../rule_schema'; -import { queryExecutor } from '../../signals/executors/query'; +import { queryExecutor } from './query'; import type { CreateQueryRuleOptions, SecurityAlertType } from '../types'; import { validateIndexPatterns } from '../utils'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/query.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/query.ts similarity index 90% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/query.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/query.ts index 7c3dc31b742ae..52157c2f64a94 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/query.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/query.ts @@ -13,18 +13,18 @@ import type { import { firstValueFrom } from 'rxjs'; import type { LicensingPluginSetup } from '@kbn/licensing-plugin/server'; -import { getFilter } from '../get_filter'; -import type { BucketHistory } from '../alert_suppression/group_and_bulk_create'; -import { groupAndBulkCreate } from '../alert_suppression/group_and_bulk_create'; -import { searchAfterAndBulkCreate } from '../search_after_bulk_create'; +import { getFilter } from '../utils/get_filter'; +import type { BucketHistory } from './alert_suppression/group_and_bulk_create'; +import { groupAndBulkCreate } from './alert_suppression/group_and_bulk_create'; +import { searchAfterAndBulkCreate } from '../utils/search_after_bulk_create'; import type { ITelemetryEventsSender } from '../../../telemetry/sender'; import type { UnifiedQueryRuleParams } from '../../rule_schema'; import type { ExperimentalFeatures } from '../../../../../common/experimental_features'; -import { buildReasonMessageForQueryAlert } from '../reason_formatters'; +import { buildReasonMessageForQueryAlert } from '../utils/reason_formatters'; import { withSecuritySpan } from '../../../../utils/with_security_span'; import { scheduleNotificationResponseActions } from '../../rule_response_actions/schedule_notification_response_actions'; import type { SetupPlugins } from '../../../../plugin_contract'; -import type { RunOpts } from '../../rule_types/types'; +import type { RunOpts } from '../types'; export const queryExecutor = async ({ runOpts, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/README.md b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/README.md similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/README.md rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/README.md diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_data_view.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/extract_data_view.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_data_view.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/extract_data_view.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_data_view.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/extract_data_view.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_data_view.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/extract_data_view.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_exceptions_list.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/extract_exceptions_list.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_exceptions_list.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/extract_exceptions_list.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_exceptions_list.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/extract_exceptions_list.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_exceptions_list.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/extract_exceptions_list.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_references.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/extract_references.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_references.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/extract_references.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_references.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/extract_references.ts similarity index 96% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_references.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/extract_references.ts index 91ceb32edfe35..066aa06864e41 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_references.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/extract_references.ts @@ -10,7 +10,7 @@ import type { RuleParamsAndRefs } from '@kbn/alerting-plugin/server'; import type { RuleParams } from '../../rule_schema'; -import { isMachineLearningParams } from '../utils'; +import { isMachineLearningParams } from '../utils/utils'; import { extractExceptionsList } from './extract_exceptions_list'; import { extractDataView } from './extract_data_view'; @@ -30,7 +30,7 @@ import { extractDataView } from './extract_data_view'; * Optionally you can remove any parameters you do not want to store within the Saved Object here: * const paramsWithoutSavedObjectReferences = { removeParam, ...otherParams }; * - * If you do remove params, then update the types in: security_solution/server/lib/detection_engine/signals/types.ts + * If you do remove params, then update the types in: security_solution/server/lib/detection_engine/rule_types/types.ts * to use an omit for the functions of "isAlertExecutor" and "SignalRuleAlertTypeDefinition" * @param logger Kibana injected logger * @param params The params of the base rule(s). diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/index.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/index.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/index.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_data_view.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/inject_data_view.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_data_view.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/inject_data_view.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_data_view.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/inject_data_view.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_data_view.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/inject_data_view.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_exceptions_list.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/inject_exceptions_list.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_exceptions_list.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/inject_exceptions_list.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_exceptions_list.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/inject_exceptions_list.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_exceptions_list.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/inject_exceptions_list.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_references.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/inject_references.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_references.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/inject_references.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_references.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/inject_references.ts similarity index 97% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_references.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/inject_references.ts index 40ad3cf3678a9..876e329734f8c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_references.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/inject_references.ts @@ -7,7 +7,7 @@ import type { Logger, SavedObjectReference } from '@kbn/core/server'; import type { RuleParams } from '../../rule_schema'; -import { isMachineLearningParams } from '../utils'; +import { isMachineLearningParams } from '../utils/utils'; import { injectExceptionsReferences } from './inject_exceptions_list'; import { injectDataViewReferences } from './inject_data_view'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/constants.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/utils/constants.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/constants.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/utils/constants.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/utils/get_saved_object_name_pattern.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/utils/get_saved_object_name_pattern.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/utils/get_saved_object_name_pattern.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/utils/get_saved_object_name_pattern.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern_for_exception_list.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/utils/get_saved_object_name_pattern_for_exception_list.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern_for_exception_list.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/utils/get_saved_object_name_pattern_for_exception_list.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern_for_exception_list.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/utils/get_saved_object_name_pattern_for_exception_list.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern_for_exception_list.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/utils/get_saved_object_name_pattern_for_exception_list.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/utils/get_saved_object_reference.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/utils/get_saved_object_reference.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/utils/get_saved_object_reference.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/utils/get_saved_object_reference.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference_for_data_view.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/utils/get_saved_object_reference_for_data_view.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference_for_data_view.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/utils/get_saved_object_reference_for_data_view.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference_for_data_view.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/utils/get_saved_object_reference_for_data_view.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference_for_data_view.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/utils/get_saved_object_reference_for_data_view.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference_for_exceptions_list.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/utils/get_saved_object_reference_for_exceptions_list.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference_for_exceptions_list.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/utils/get_saved_object_reference_for_exceptions_list.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference_for_exceptions_list.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/utils/get_saved_object_reference_for_exceptions_list.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference_for_exceptions_list.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/utils/get_saved_object_reference_for_exceptions_list.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/utils/index.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/index.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/utils/index.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_missing_saved_object_error.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/utils/log_missing_saved_object_error.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_missing_saved_object_error.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/utils/log_missing_saved_object_error.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_missing_saved_object_error.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/utils/log_missing_saved_object_error.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_missing_saved_object_error.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_object_references/utils/log_missing_saved_object_error.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/__snapshots__/get_threshold_signal_history.test.ts.snap b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/__snapshots__/get_threshold_signal_history.test.ts.snap similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/__snapshots__/get_threshold_signal_history.test.ts.snap rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/__snapshots__/get_threshold_signal_history.test.ts.snap diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/build_signal_history.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/build_signal_history.test.ts similarity index 93% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/build_signal_history.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/build_signal_history.test.ts index 8196fa229c65c..5d36e6ebad2d9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/build_signal_history.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/build_signal_history.test.ts @@ -6,7 +6,7 @@ */ import { ALERT_ORIGINAL_TIME } from '../../../../../common/field_maps/field_names'; -import { sampleThresholdAlert } from '../../rule_types/__mocks__/threshold'; +import { sampleThresholdAlert } from '../__mocks__/threshold'; import { buildThresholdSignalHistory } from './build_signal_history'; describe('buildSignalHistory', () => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/build_signal_history.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/build_signal_history.ts similarity index 91% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/build_signal_history.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/build_signal_history.ts index 8497b94397d9b..e6390fe7f67ea 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/build_signal_history.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/build_signal_history.ts @@ -9,8 +9,10 @@ import type { SearchHit } from '@elastic/elasticsearch/lib/api/typesWithBodyKey' import { ALERT_RULE_PARAMETERS } from '@kbn/rule-data-utils'; import { ALERT_ORIGINAL_TIME } from '../../../../../common/field_maps/field_names'; -import type { SimpleHit, ThresholdSignalHistory } from '../types'; -import { getThresholdTermsHash, isWrappedDetectionAlert, isWrappedSignalHit } from '../utils'; +import type { SimpleHit } from '../types'; +import type { ThresholdSignalHistory } from './types'; +import { getThresholdTermsHash } from './utils'; +import { isWrappedDetectionAlert, isWrappedSignalHit } from '../utils/utils'; interface GetThresholdSignalHistoryParams { alerts: Array>; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/build_threshold_aggregation.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/build_threshold_aggregation.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/build_threshold_aggregation.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/build_threshold_aggregation.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/build_threshold_aggregation.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/build_threshold_aggregation.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/build_threshold_aggregation.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/build_threshold_aggregation.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/bulk_create_threshold_signals.test.ts similarity index 98% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/bulk_create_threshold_signals.test.ts index 1fa0baaedfa13..1e709b9b47017 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/bulk_create_threshold_signals.test.ts @@ -6,7 +6,7 @@ */ import type { ThresholdNormalized } from '../../../../../common/detection_engine/rule_schema'; -import { calculateThresholdSignalUuid } from '../utils'; +import { calculateThresholdSignalUuid } from './utils'; import { getTransformedHits } from './bulk_create_threshold_signals'; describe('transformThresholdNormalizedResultsToEcs', () => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/bulk_create_threshold_signals.ts similarity index 88% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/bulk_create_threshold_signals.ts index 22ebcf8c1ced0..1bef127097e60 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/bulk_create_threshold_signals.ts @@ -13,14 +13,14 @@ import type { RuleExecutorServices, } from '@kbn/alerting-plugin/server'; import type { ThresholdNormalized } from '../../../../../common/detection_engine/rule_schema'; -import type { GenericBulkCreateResponse } from '../../rule_types/factories/bulk_create_factory'; -import { calculateThresholdSignalUuid } from '../utils'; -import { buildReasonMessageForThresholdAlert } from '../reason_formatters'; -import type { ThresholdSignalHistory, BulkCreate, WrapHits } from '../types'; +import type { GenericBulkCreateResponse } from '../factories/bulk_create_factory'; +import { calculateThresholdSignalUuid } from './utils'; +import { buildReasonMessageForThresholdAlert } from '../utils/reason_formatters'; +import type { ThresholdSignalHistory, ThresholdBucket } from './types'; +import type { BulkCreate, WrapHits } from '../types'; import type { CompleteRule, ThresholdRuleParams } from '../../rule_schema'; import type { BaseFieldsLatest } from '../../../../../common/detection_engine/schemas/alerts'; -import type { ThresholdBucket } from './types'; -import { createEnrichEventsFunction } from '../enrichments'; +import { createEnrichEventsFunction } from '../utils/enrichments'; import type { IRuleExecutionLogForExecutors } from '../../rule_monitoring'; interface BulkCreateThresholdSignalsParams { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/create_threshold_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/create_threshold_alert_type.ts index b465abed15977..273c3abd3966f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/create_threshold_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/create_threshold_alert_type.ts @@ -11,8 +11,8 @@ import { SERVER_APP_ID } from '../../../../../common/constants'; import type { ThresholdRuleParams } from '../../rule_schema'; import { thresholdRuleParams } from '../../rule_schema'; -import { thresholdExecutor } from '../../signals/executors/threshold'; -import type { ThresholdAlertState } from '../../signals/types'; +import { thresholdExecutor } from './threshold'; +import type { ThresholdAlertState } from './types'; import type { CreateRuleOptions, SecurityAlertType } from '../types'; import { validateIndexPatterns } from '../utils'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/find_threshold_signals.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/find_threshold_signals.test.ts similarity index 97% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/find_threshold_signals.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/find_threshold_signals.test.ts index 970010a3bce43..ab19edade87f7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/find_threshold_signals.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/find_threshold_signals.test.ts @@ -8,13 +8,13 @@ import type { RuleExecutorServicesMock } from '@kbn/alerting-plugin/server/mocks'; import { alertsMock } from '@kbn/alerting-plugin/server/mocks'; import { sampleEmptyDocSearchResults } from '../__mocks__/es_results'; -import * as single_search_after from '../single_search_after'; +import * as single_search_after from '../utils/single_search_after'; import { findThresholdSignals } from './find_threshold_signals'; import { TIMESTAMP } from '@kbn/rule-data-utils'; import { ruleExecutionLogMock } from '../../rule_monitoring/mocks'; -import { buildTimestampRuntimeMapping } from '../../rule_types/utils'; -import { TIMESTAMP_RUNTIME_FIELD } from '../../rule_types/constants'; -import { getQueryFilter } from '../get_query_filter'; +import { buildTimestampRuntimeMapping } from '../utils'; +import { TIMESTAMP_RUNTIME_FIELD } from '../constants'; +import { getQueryFilter } from '../utils/get_query_filter'; import type { ESBoolQuery } from '../../../../../common/typed_json'; const mockSingleSearchAfter = jest.fn(async () => ({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/find_threshold_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/find_threshold_signals.ts similarity index 98% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/find_threshold_signals.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/find_threshold_signals.ts index dba72e0560734..758fe7b161d68 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/find_threshold_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/find_threshold_signals.ts @@ -18,7 +18,7 @@ import type { ThresholdNormalized, TimestampOverride, } from '../../../../../common/detection_engine/rule_schema'; -import { singleSearchAfter } from '../single_search_after'; +import { singleSearchAfter } from '../utils/single_search_after'; import { buildThresholdMultiBucketAggregation, buildThresholdSingleBucketAggregation, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/get_threshold_bucket_filters.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/get_threshold_bucket_filters.test.ts similarity index 98% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/get_threshold_bucket_filters.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/get_threshold_bucket_filters.test.ts index b38351751a717..299810c169f89 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/get_threshold_bucket_filters.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/get_threshold_bucket_filters.test.ts @@ -6,7 +6,7 @@ */ import { TIMESTAMP } from '@kbn/rule-data-utils'; -import { sampleThresholdSignalHistory } from '../__mocks__/threshold_signal_history.mock'; +import { sampleThresholdSignalHistory } from '../__mocks__/threshold'; import { getThresholdBucketFilters } from './get_threshold_bucket_filters'; describe('getThresholdBucketFilters', () => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/get_threshold_bucket_filters.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/get_threshold_bucket_filters.ts similarity index 98% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/get_threshold_bucket_filters.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/get_threshold_bucket_filters.ts index dfd61e307bd43..7bb04a8efa5b1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/get_threshold_bucket_filters.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/get_threshold_bucket_filters.ts @@ -7,7 +7,7 @@ import type { Filter } from '@kbn/es-query'; import type { ESFilter } from '@kbn/es-types'; -import type { ThresholdSignalHistory, ThresholdSignalHistoryRecord } from '../types'; +import type { ThresholdSignalHistory, ThresholdSignalHistoryRecord } from './types'; /* * Returns a filter to exclude events that have already been included in a diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/get_threshold_signal_history.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/get_threshold_signal_history.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/get_threshold_signal_history.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/get_threshold_signal_history.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/get_threshold_signal_history.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/get_threshold_signal_history.ts similarity index 96% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/get_threshold_signal_history.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/get_threshold_signal_history.ts index 4826cd574a906..157300796a8ee 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/get_threshold_signal_history.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/get_threshold_signal_history.ts @@ -8,9 +8,9 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { IRuleDataReader } from '@kbn/rule-registry-plugin/server'; import { ALERT_RULE_UUID } from '@kbn/rule-data-utils'; -import type { ThresholdSignalHistory } from '../types'; +import type { ThresholdSignalHistory } from './types'; import { buildThresholdSignalHistory } from './build_signal_history'; -import { createErrorsFromShard } from '../utils'; +import { createErrorsFromShard } from '../utils/utils'; interface GetThresholdSignalHistoryParams { from: string; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/threshold.test.ts similarity index 99% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/threshold.test.ts index 0e780d9709ab8..2a4f54d23271e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/threshold.test.ts @@ -13,7 +13,7 @@ import { alertsMock } from '@kbn/alerting-plugin/server/mocks'; import { thresholdExecutor } from './threshold'; import { getThresholdRuleParams, getCompleteRuleMock } from '../../rule_schema/mocks'; import { sampleEmptyAggsSearchResults } from '../__mocks__/es_results'; -import { getThresholdTermsHash } from '../utils'; +import { getThresholdTermsHash } from './utils'; import type { ThresholdRuleParams } from '../../rule_schema'; import { createRuleDataClientMock } from '@kbn/rule-registry-plugin/server/rule_data_client/rule_data_client.mock'; import { TIMESTAMP } from '@kbn/rule-data-utils'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/threshold.ts similarity index 91% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/threshold.ts index 515caf5dcd5e1..670b3e5011a29 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/threshold.ts @@ -17,27 +17,26 @@ import type { import type { IRuleDataReader } from '@kbn/rule-registry-plugin/server'; import type { Filter } from '@kbn/es-query'; import type { CompleteRule, ThresholdRuleParams } from '../../rule_schema'; -import { getFilter } from '../get_filter'; -import { - bulkCreateThresholdSignals, - findThresholdSignals, - getThresholdBucketFilters, - getThresholdSignalHistory, -} from '../threshold'; +import { getFilter } from '../utils/get_filter'; +import { bulkCreateThresholdSignals } from './bulk_create_threshold_signals'; +import { findThresholdSignals } from './find_threshold_signals'; +import { getThresholdBucketFilters } from './get_threshold_bucket_filters'; +import { getThresholdSignalHistory } from './get_threshold_signal_history'; + import type { BulkCreate, RuleRangeTuple, SearchAfterAndBulkCreateReturnType, - ThresholdAlertState, WrapHits, } from '../types'; +import type { ThresholdAlertState } from './types'; import { addToSearchAfterReturn, createSearchAfterReturnType, getUnprocessedExceptionsWarnings, -} from '../utils'; +} from '../utils/utils'; import { withSecuritySpan } from '../../../../utils/with_security_span'; -import { buildThresholdSignalHistory } from '../threshold/build_signal_history'; +import { buildThresholdSignalHistory } from './build_signal_history'; import type { IRuleExecutionLogForExecutors } from '../../rule_monitoring'; export const thresholdExecutor = async ({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/types.ts similarity index 66% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/types.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/types.ts index d6a68833ed263..14e244000a30d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/types.ts @@ -12,7 +12,9 @@ import type { AggregationsMinAggregate, } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { ESSearchResponse } from '@kbn/es-types'; +import type { RuleTypeState } from '@kbn/alerting-plugin/server'; import type { SignalSource } from '../types'; +import type { SearchTypes } from '../../../../../common/detection_engine/types'; import type { buildThresholdMultiBucketAggregation, buildThresholdSingleBucketAggregation, @@ -44,3 +46,33 @@ export type ThresholdSingleBucketAggregationResult = ESSearchResponse< export type ThresholdCompositeBucket = AggregationsCompositeBucket & ThresholdLeafAggregates; export type ThresholdBucket = ThresholdCompositeBucket; + +export interface ThresholdResult { + terms?: Array<{ + field: string; + value: string; + }>; + cardinality?: Array<{ + field: string; + value: number; + }>; + count: number; + from: string; +} + +export interface ThresholdSignalHistoryRecord { + terms: Array<{ + field?: string; + value: SearchTypes; + }>; + lastSignalTimestamp: number; +} + +export interface ThresholdSignalHistory { + [hash: string]: ThresholdSignalHistoryRecord; +} + +export interface ThresholdAlertState extends RuleTypeState { + initialized: boolean; + signalHistory: ThresholdSignalHistory; +} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/utils.test.ts new file mode 100644 index 0000000000000..0022e88217f8b --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/utils.test.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { calculateThresholdSignalUuid } from './utils'; + +describe('calculateThresholdSignalUuid', () => { + it('should generate a uuid without key', () => { + const startedAt = new Date('2020-12-17T16:27:00Z'); + const signalUuid = calculateThresholdSignalUuid('abcd', startedAt, ['agent.name']); + expect(signalUuid).toEqual('a4832768-a379-583a-b1a2-e2ce2ad9e6e9'); + }); + + it('should generate a uuid with key', () => { + const startedAt = new Date('2019-11-18T13:32:00Z'); + const signalUuid = calculateThresholdSignalUuid('abcd', startedAt, ['host.ip'], '1.2.3.4'); + expect(signalUuid).toEqual('ee8870dc-45ff-5e6c-a2f9-80886651ce03'); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/utils.ts new file mode 100644 index 0000000000000..1c4f85e250a1f --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/utils.ts @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createHash } from 'crypto'; +import { v5 as uuidv5 } from 'uuid'; +import type { + ThresholdNormalized, + ThresholdWithCardinality, +} from '../../../../../common/detection_engine/rule_schema'; + +export const shouldFilterByCardinality = ( + threshold: ThresholdNormalized +): threshold is ThresholdWithCardinality => !!threshold.cardinality?.length; + +export const calculateThresholdSignalUuid = ( + ruleId: string, + startedAt: Date, + thresholdFields: string[], + key?: string +): string => { + // used to generate stable Threshold Signals ID when run with the same params + const NAMESPACE_ID = '0684ec03-7201-4ee0-8ee0-3a3f6b2479b2'; + + const startedAtString = startedAt.toISOString(); + const keyString = key ?? ''; + const baseString = `${ruleId}${startedAtString}${thresholdFields.join(',')}${keyString}`; + + return uuidv5(baseString, NAMESPACE_ID); +}; + +export const getThresholdTermsHash = ( + terms: Array<{ + field: string; + value: string; + }> +): string => { + return createHash('sha256') + .update( + terms + .sort((term1, term2) => (term1.field > term2.field ? 1 : -1)) + .map((term) => { + return `${term.field}:${term.value}`; + }) + .join(',') + ) + .digest('hex'); +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts index 614236391d041..36ac78f2f74d5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts @@ -13,13 +13,15 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { QUERY_RULE_TYPE_ID, SAVED_QUERY_RULE_TYPE_ID } from '@kbn/securitysolution-rules'; -import type { RuleExecutorOptions, RuleType } from '@kbn/alerting-plugin/server'; import type { - AlertInstanceContext, - AlertInstanceState, + RuleExecutorOptions, + RuleType, RuleTypeState, - WithoutReservedActionGroups, -} from '@kbn/alerting-plugin/common'; + AlertInstanceState, + AlertInstanceContext, + RuleExecutorServices, +} from '@kbn/alerting-plugin/server'; +import type { WithoutReservedActionGroups } from '@kbn/alerting-plugin/common'; import type { ListClient } from '@kbn/lists-plugin/server'; import type { PersistenceServices, @@ -27,23 +29,37 @@ import type { IRuleDataReader, SuppressedAlertService, } from '@kbn/rule-registry-plugin/server'; +import type { EcsFieldMap } from '@kbn/rule-registry-plugin/common/assets/field_maps/ecs_field_map'; +import type { TypeOfFieldMap } from '@kbn/rule-registry-plugin/common/field_map'; import type { LicensingPluginSetup } from '@kbn/licensing-plugin/server'; - import type { Filter } from '@kbn/es-query'; + import type { ConfigType } from '../../../config'; import type { SetupPlugins } from '../../../plugin'; import type { CompleteRule, RuleParams } from '../rule_schema'; -import type { - BulkCreate, - SearchAfterAndBulkCreateReturnType, - WrapHits, - WrapSequences, -} from '../signals/types'; import type { ExperimentalFeatures } from '../../../../common/experimental_features'; import type { ITelemetryEventsSender } from '../../telemetry/sender'; import type { IRuleExecutionLogForExecutors, IRuleExecutionLogService } from '../rule_monitoring'; import type { RefreshTypes } from '../types'; +import type { Status } from '../../../../common/detection_engine/schemas/common/schemas'; +import type { + BaseHit, + RuleAlertAction, + SearchTypes, + EqlSequence, +} from '../../../../common/detection_engine/types'; +import type { GenericBulkCreateResponse } from './factories'; +import type { BuildReasonMessage } from './utils/reason_formatters'; +import type { + BaseFieldsLatest, + DetectionAlert, + WrappedFieldsLatest, +} from '../../../../common/detection_engine/schemas/alerts'; +import type { RuleResponse } from '../../../../common/detection_engine/rule_schema'; +import type { EnrichEvents } from './utils/enrichments/types'; +import type { ThresholdResult } from './threshold/types'; + export interface SecurityAlertTypeReturnValue { bulkCreateTimes: string[]; enrichmentTimes: string[]; @@ -147,3 +163,223 @@ export interface CreateQueryRuleOptions id: typeof QUERY_RULE_TYPE_ID | typeof SAVED_QUERY_RULE_TYPE_ID; name: 'Custom Query Rule' | 'Saved Query Rule'; } + +export interface RuleRangeTuple { + to: moment.Moment; + from: moment.Moment; + maxSignals: number; +} + +/** + * SignalSource is being used as both a type for documents that match detection engine queries as well as + * for queries that could be on top of signals. In cases where it is matched against detection engine queries, + * '@timestamp' might not be there since it is not required and we have timestamp override capabilities. Also + * the signal addition object, "signal?: {" will not be there unless it's a conflicting field when we are running + * queries on events. + * + * For cases where we are running queries against signals (signals on signals) "@timestamp" should always be there + * and the "signal?: {" sub-object should always be there. + */ +export interface SignalSource { + [key: string]: SearchTypes; + '@timestamp'?: string; + signal?: { + /** + * "parent" is deprecated: new signals should populate "parents" instead. Both are optional + * until all signals with parent are gone and we can safely remove it. + * @deprecated Use parents instead + */ + parent?: Ancestor; + parents?: Ancestor[]; + ancestors: Ancestor[]; + group?: { + id: string; + index?: number; + }; + rule: { + id: string; + description?: string; + false_positives?: string[]; + immutable?: boolean; + }; + /** signal.depth was introduced in 7.10 and pre-7.10 signals do not have it. */ + depth?: number; + original_time?: string; + /** signal.reason was introduced in 7.15 and pre-7.15 signals do not have it. */ + reason?: string; + status?: string; + threshold_result?: ThresholdResult; + }; + kibana?: SearchTypes; +} + +export interface BulkItem { + create?: { + _index: string; + _type?: string; + _id: string; + _version: number; + result?: string; + _shards?: { + total: number; + successful: number; + failed: number; + }; + _seq_no?: number; + _primary_term?: number; + status: number; + error?: { + type: string; + reason: string; + index_uuid?: string; + shard: string; + index: string; + }; + }; +} + +export interface BulkResponse { + took: number; + errors: boolean; + items: BulkItem[]; +} + +export type EventHit = Exclude, '@timestamp'> & { + '@timestamp': string; + [key: string]: SearchTypes; +}; +export type WrappedEventHit = BaseHit; + +export type SignalSearchResponse< + TAggregations = Record +> = estypes.SearchResponse; +export type SignalSourceHit = estypes.SearchHit; +export type AlertSourceHit = estypes.SearchHit; +export type WrappedSignalHit = BaseHit; +export type BaseSignalHit = estypes.SearchHit; + +export interface Ancestor { + rule?: string; + id: string; + type: string; + index: string; + depth: number; +} + +export interface Signal { + _meta?: { + version: number; + }; + rule: RuleResponse; + /** + * @deprecated Use "parents" instead of "parent" + */ + parent?: Ancestor; + parents: Ancestor[]; + ancestors: Ancestor[]; + group?: { + id: string; + index?: number; + }; + original_time?: string; + original_event?: SearchTypes; + reason?: string; + status: Status; + threshold_result?: ThresholdResult; + original_signal?: SearchTypes; + depth: number; +} + +export interface SignalHit { + '@timestamp': string; + event: object; + signal: Signal; + [key: string]: SearchTypes; +} + +export interface AlertAttributes { + actions: RuleAlertAction[]; + alertTypeId: string; + enabled: boolean; + name: string; + tags: string[]; + createdBy: string; + createdAt: string; + updatedBy: string; + schedule: { + interval: string; + }; + throttle: string; + params: T; +} + +export type BulkResponseErrorAggregation = Record; + +export type SignalsEnrichment = (signals: SignalSourceHit[]) => Promise; + +export type BulkCreate = ( + docs: Array>, + maxAlerts?: number, + enrichEvents?: EnrichEvents +) => Promise>; + +export type SimpleHit = BaseHit<{ '@timestamp'?: string }>; + +export type WrapHits = ( + hits: Array>, + buildReasonMessage: BuildReasonMessage +) => Array>; + +export type WrapSequences = ( + sequences: Array>, + buildReasonMessage: BuildReasonMessage +) => Array>; + +export type RuleServices = RuleExecutorServices< + AlertInstanceState, + AlertInstanceContext, + 'default' +>; +export interface SearchAfterAndBulkCreateParams { + tuple: { + to: moment.Moment; + from: moment.Moment; + maxSignals: number; + }; + services: RuleServices; + listClient: ListClient; + exceptionsList: ExceptionListItemSchema[]; + ruleExecutionLogger: IRuleExecutionLogForExecutors; + eventsTelemetry: ITelemetryEventsSender | undefined; + inputIndexPattern: string[]; + pageSize: number; + filter: estypes.QueryDslQueryContainer; + buildReasonMessage: BuildReasonMessage; + enrichment?: SignalsEnrichment; + bulkCreate: BulkCreate; + wrapHits: WrapHits; + trackTotalHits?: boolean; + sortOrder?: estypes.SortOrder; + runtimeMappings: estypes.MappingRuntimeFields | undefined; + primaryTimestamp: string; + secondaryTimestamp?: string; +} + +export interface SearchAfterAndBulkCreateReturnType { + success: boolean; + warning: boolean; + searchAfterTimes: string[]; + enrichmentTimes: string[]; + bulkCreateTimes: string[]; + lastLookBackDate: Date | null | undefined; + createdSignalsCount: number; + createdSignals: unknown[]; + errors: string[]; + warningMessages: string[]; +} + +// the new fields can be added later if needed +export interface OverrideBodyQuery { + _source?: estypes.SearchSourceConfig; + fields?: estypes.Fields; +} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_events_query.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/build_events_query.test.ts similarity index 51% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_events_query.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/build_events_query.test.ts index 8aa51719a69bb..03ee439c2c969 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_events_query.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/build_events_query.test.ts @@ -5,19 +5,7 @@ * 2.0. */ -import { buildEqlSearchRequest, buildEventsSearchQuery } from './build_events_query'; -import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; -import { getListClientMock } from '@kbn/lists-plugin/server/services/lists/list_client.mock'; -import { buildExceptionFilter } from '@kbn/lists-plugin/server/services/exception_lists'; - -const emptyFilter = { - bool: { - must: [], - filter: [], - should: [], - must_not: [], - }, -}; +import { buildEventsSearchQuery } from './build_events_query'; describe('create_signals', () => { test('it builds a now-5m up to today filter', () => { @@ -588,416 +576,4 @@ describe('create_signals', () => { }, }); }); - - describe('buildEqlSearchRequest', () => { - test('should build a basic request with time range', () => { - const request = buildEqlSearchRequest({ - query: 'process where true', - index: ['testindex1', 'testindex2'], - from: 'now-5m', - to: 'now', - size: 100, - filters: undefined, - primaryTimestamp: '@timestamp', - secondaryTimestamp: undefined, - runtimeMappings: undefined, - eventCategoryOverride: undefined, - exceptionFilter: undefined, - }); - expect(request).toEqual({ - allow_no_indices: true, - index: ['testindex1', 'testindex2'], - body: { - size: 100, - query: 'process where true', - runtime_mappings: undefined, - filter: { - bool: { - filter: [ - { - range: { - '@timestamp': { - gte: 'now-5m', - lte: 'now', - format: 'strict_date_optional_time', - }, - }, - }, - emptyFilter, - ], - }, - }, - fields: [ - { - field: '*', - include_unmapped: true, - }, - { - field: '@timestamp', - format: 'strict_date_optional_time', - }, - ], - }, - }); - }); - - test('should build a request with timestamp and event category overrides', () => { - const request = buildEqlSearchRequest({ - query: 'process where true', - index: ['testindex1', 'testindex2'], - from: 'now-5m', - to: 'now', - size: 100, - filters: undefined, - primaryTimestamp: 'event.ingested', - secondaryTimestamp: '@timestamp', - runtimeMappings: undefined, - eventCategoryOverride: 'event.other_category', - timestampField: undefined, - exceptionFilter: undefined, - }); - expect(request).toEqual({ - allow_no_indices: true, - index: ['testindex1', 'testindex2'], - body: { - event_category_field: 'event.other_category', - size: 100, - query: 'process where true', - runtime_mappings: undefined, - filter: { - bool: { - filter: [ - { - bool: { - minimum_should_match: 1, - should: [ - { - range: { - 'event.ingested': { - lte: 'now', - gte: 'now-5m', - format: 'strict_date_optional_time', - }, - }, - }, - { - bool: { - filter: [ - { - range: { - '@timestamp': { - lte: 'now', - gte: 'now-5m', - format: 'strict_date_optional_time', - }, - }, - }, - { - bool: { - must_not: { - exists: { - field: 'event.ingested', - }, - }, - }, - }, - ], - }, - }, - ], - }, - }, - emptyFilter, - ], - }, - }, - fields: [ - { - field: '*', - include_unmapped: true, - }, - { - field: 'event.ingested', - format: 'strict_date_optional_time', - }, - { - field: '@timestamp', - format: 'strict_date_optional_time', - }, - ], - }, - }); - }); - - test('should build a request without @timestamp fallback if secondaryTimestamp is not specified', () => { - const request = buildEqlSearchRequest({ - query: 'process where true', - index: ['testindex1', 'testindex2'], - from: 'now-5m', - to: 'now', - size: 100, - filters: undefined, - primaryTimestamp: 'event.ingested', - secondaryTimestamp: undefined, - runtimeMappings: undefined, - eventCategoryOverride: 'event.other_category', - timestampField: undefined, - exceptionFilter: undefined, - }); - expect(request).toEqual({ - allow_no_indices: true, - index: ['testindex1', 'testindex2'], - body: { - event_category_field: 'event.other_category', - size: 100, - query: 'process where true', - runtime_mappings: undefined, - filter: { - bool: { - filter: [ - { - range: { - 'event.ingested': { - lte: 'now', - gte: 'now-5m', - format: 'strict_date_optional_time', - }, - }, - }, - emptyFilter, - ], - }, - }, - fields: [ - { - field: '*', - include_unmapped: true, - }, - { - field: 'event.ingested', - format: 'strict_date_optional_time', - }, - ], - }, - }); - }); - - test('should build a request with exceptions', async () => { - const { filter } = await buildExceptionFilter({ - listClient: getListClientMock(), - lists: [getExceptionListItemSchemaMock()], - alias: null, - chunkSize: 1024, - excludeExceptions: true, - startedAt: new Date(), - }); - const request = buildEqlSearchRequest({ - query: 'process where true', - index: ['testindex1', 'testindex2'], - from: 'now-5m', - to: 'now', - size: 100, - filters: undefined, - primaryTimestamp: '@timestamp', - secondaryTimestamp: undefined, - runtimeMappings: undefined, - eventCategoryOverride: undefined, - exceptionFilter: filter, - }); - expect(request).toMatchInlineSnapshot(` - Object { - "allow_no_indices": true, - "body": Object { - "event_category_field": undefined, - "fields": Array [ - Object { - "field": "*", - "include_unmapped": true, - }, - Object { - "field": "@timestamp", - "format": "strict_date_optional_time", - }, - ], - "filter": Object { - "bool": Object { - "filter": Array [ - Object { - "range": Object { - "@timestamp": Object { - "format": "strict_date_optional_time", - "gte": "now-5m", - "lte": "now", - }, - }, - }, - Object { - "bool": Object { - "filter": Array [], - "must": Array [], - "must_not": Array [ - Object { - "bool": Object { - "should": Array [ - Object { - "bool": Object { - "filter": Array [ - Object { - "nested": Object { - "path": "some.parentField", - "query": Object { - "bool": Object { - "minimum_should_match": 1, - "should": Array [ - Object { - "match_phrase": Object { - "some.parentField.nested.field": "some value", - }, - }, - ], - }, - }, - "score_mode": "none", - }, - }, - Object { - "bool": Object { - "minimum_should_match": 1, - "should": Array [ - Object { - "match_phrase": Object { - "some.not.nested.field": "some value", - }, - }, - ], - }, - }, - ], - }, - }, - ], - }, - }, - ], - "should": Array [], - }, - }, - ], - }, - }, - "query": "process where true", - "runtime_mappings": undefined, - "size": 100, - "tiebreaker_field": undefined, - "timestamp_field": undefined, - }, - "index": Array [ - "testindex1", - "testindex2", - ], - } - `); - }); - - test('should build a request with filters', () => { - const filters = [ - { - meta: { - alias: null, - negate: false, - disabled: false, - type: 'exists', - key: 'process.name', - value: 'exists', - }, - query: { - exists: { - field: 'process.name', - }, - }, - }, - { - meta: { - alias: null, - negate: false, - disabled: false, - type: 'phrase', - key: 'host.name', - params: { - query: 'Host-b4d9hu1a56', - }, - }, - query: { - match_phrase: { - 'host.name': 'Host-b4d9hu1a56', - }, - }, - }, - ]; - const request = buildEqlSearchRequest({ - query: 'process where true', - index: ['testindex1', 'testindex2'], - from: 'now-5m', - to: 'now', - size: 100, - filters, - primaryTimestamp: '@timestamp', - secondaryTimestamp: undefined, - runtimeMappings: undefined, - exceptionFilter: undefined, - }); - expect(request).toEqual({ - allow_no_indices: true, - index: ['testindex1', 'testindex2'], - body: { - size: 100, - query: 'process where true', - filter: { - bool: { - filter: [ - { - range: { - '@timestamp': { - gte: 'now-5m', - lte: 'now', - format: 'strict_date_optional_time', - }, - }, - }, - { - bool: { - must: [], - filter: [ - { - exists: { - field: 'process.name', - }, - }, - { - match_phrase: { - 'host.name': 'Host-b4d9hu1a56', - }, - }, - ], - should: [], - must_not: [], - }, - }, - ], - }, - }, - fields: [ - { - field: '*', - include_unmapped: true, - }, - { - field: '@timestamp', - format: 'strict_date_optional_time', - }, - ], - }, - }); - }); - }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_events_query.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/build_events_query.ts similarity index 71% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_events_query.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/build_events_query.ts index a04e27c1f8837..3f0cac4d7089b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_events_query.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/build_events_query.ts @@ -6,13 +6,8 @@ */ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { isEmpty } from 'lodash'; -import type { Filter } from '@kbn/es-query'; -import type { OverrideBodyQuery } from './types'; -import type { - RuleFilterArray, - TimestampOverride, -} from '../../../../common/detection_engine/rule_schema'; -import { getQueryFilter } from './get_query_filter'; +import type { OverrideBodyQuery } from '../types'; +import type { TimestampOverride } from '../../../../../common/detection_engine/rule_schema'; interface BuildEventsSearchQuery { aggregations?: Record; @@ -31,22 +26,6 @@ interface BuildEventsSearchQuery { overrideBody?: OverrideBodyQuery; } -interface BuildEqlSearchRequestParams { - query: string; - index: string[]; - from: string; - to: string; - size: number; - filters: RuleFilterArray | undefined; - primaryTimestamp: TimestampOverride; - secondaryTimestamp: TimestampOverride | undefined; - runtimeMappings: estypes.MappingRuntimeFields | undefined; - eventCategoryOverride?: string; - timestampField?: string; - tiebreakerField?: string; - exceptionFilter: Filter | undefined; -} - export const buildTimeRangeFilter = ({ to, from, @@ -211,68 +190,3 @@ export const buildEventsSearchQuery = ({ } return searchQuery; }; - -export const buildEqlSearchRequest = ({ - query, - index, - from, - to, - size, - filters, - primaryTimestamp, - secondaryTimestamp, - runtimeMappings, - eventCategoryOverride, - timestampField, - tiebreakerField, - exceptionFilter, -}: BuildEqlSearchRequestParams): estypes.EqlSearchRequest => { - const timestamps = secondaryTimestamp - ? [primaryTimestamp, secondaryTimestamp] - : [primaryTimestamp]; - const docFields = timestamps.map((tstamp) => ({ - field: tstamp, - format: 'strict_date_optional_time', - })); - - const esFilter = getQueryFilter({ - query: '', - language: 'eql', - filters: filters || [], - index, - exceptionFilter, - }); - - const rangeFilter = buildTimeRangeFilter({ - to, - from, - primaryTimestamp, - secondaryTimestamp, - }); - const requestFilter: estypes.QueryDslQueryContainer[] = [rangeFilter, esFilter]; - const fields = [ - { - field: '*', - include_unmapped: true, - }, - ...docFields, - ]; - return { - index, - allow_no_indices: true, - body: { - size, - query, - filter: { - bool: { - filter: requestFilter, - }, - }, - runtime_mappings: runtimeMappings, - timestamp_field: timestampField, - event_category_field: eventCategoryOverride, - tiebreaker_field: tiebreakerField, - fields, - }, - }; -}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/__mocks__/alerts.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/__mocks__/alerts.ts similarity index 97% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/__mocks__/alerts.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/__mocks__/alerts.ts index f837553b8b062..43cd37aca396a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/__mocks__/alerts.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/__mocks__/alerts.ts @@ -46,7 +46,7 @@ import { } from '@kbn/rule-data-utils'; import type { EventsForEnrichment } from '../types'; -import type { BaseFieldsLatest } from '../../../../../../common/detection_engine/schemas/alerts'; +import type { BaseFieldsLatest } from '../../../../../../../common/detection_engine/schemas/alerts'; import { ALERT_ANCESTORS, @@ -65,7 +65,7 @@ import { ALERT_RULE_TIMELINE_TITLE, ALERT_RULE_INDICES, ALERT_RULE_TIMESTAMP_OVERRIDE, -} from '../../../../../../common/field_maps/field_names'; +} from '../../../../../../../common/field_maps/field_names'; export const createAlert = ( someUuid: string = '1', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/create_single_field_match_enrichment.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/create_single_field_match_enrichment.test.ts similarity index 98% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/create_single_field_match_enrichment.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/create_single_field_match_enrichment.test.ts index d7e29d51a0025..8134bb9380660 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/create_single_field_match_enrichment.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/create_single_field_match_enrichment.test.ts @@ -9,7 +9,7 @@ import type { RuleExecutorServicesMock } from '@kbn/alerting-plugin/server/mocks import { alertsMock } from '@kbn/alerting-plugin/server/mocks'; import { createSingleFieldMatchEnrichment } from './create_single_field_match_enrichment'; import { searchEnrichments } from './search_enrichments'; -import { ruleExecutionLogMock } from '../../rule_monitoring/mocks'; +import { ruleExecutionLogMock } from '../../../rule_monitoring/mocks'; import { createAlert } from './__mocks__/alerts'; import type { EnrichmentFunction } from './types'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/create_single_field_match_enrichment.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/create_single_field_match_enrichment.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/create_single_field_match_enrichment.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/create_single_field_match_enrichment.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/enrichment_by_type/host_risk.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/enrichment_by_type/host_risk.ts similarity index 89% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/enrichment_by_type/host_risk.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/enrichment_by_type/host_risk.ts index fb061d06ce1e2..4d5ef11788aea 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/enrichment_by_type/host_risk.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/enrichment_by_type/host_risk.ts @@ -7,8 +7,8 @@ import { set } from '@kbn/safer-lodash-set'; import { cloneDeep } from 'lodash'; -import { getHostRiskIndex } from '../../../../../../common/search_strategy/security_solution/risk_score/common'; -import { RiskScoreFields } from '../../../../../../common/search_strategy/security_solution/risk_score/all'; +import { getHostRiskIndex } from '../../../../../../../common/search_strategy/security_solution/risk_score/common'; +import { RiskScoreFields } from '../../../../../../../common/search_strategy/security_solution/risk_score/all'; import { createSingleFieldMatchEnrichment } from '../create_single_field_match_enrichment'; import type { CreateRiskEnrichment, GetIsRiskScoreAvailable } from '../types'; import { getFieldValue } from '../utils/events'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/enrichment_by_type/user_risk.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/enrichment_by_type/user_risk.ts similarity index 89% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/enrichment_by_type/user_risk.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/enrichment_by_type/user_risk.ts index db959a7671546..e80b61dfa5267 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/enrichment_by_type/user_risk.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/enrichment_by_type/user_risk.ts @@ -6,8 +6,8 @@ */ import { set } from '@kbn/safer-lodash-set'; import { cloneDeep } from 'lodash'; -import { getUserRiskIndex } from '../../../../../../common/search_strategy/security_solution/risk_score/common'; -import { RiskScoreFields } from '../../../../../../common/search_strategy/security_solution/risk_score/all'; +import { getUserRiskIndex } from '../../../../../../../common/search_strategy/security_solution/risk_score/common'; +import { RiskScoreFields } from '../../../../../../../common/search_strategy/security_solution/risk_score/all'; import { createSingleFieldMatchEnrichment } from '../create_single_field_match_enrichment'; import type { CreateRiskEnrichment, GetIsRiskScoreAvailable } from '../types'; import { getFieldValue } from '../utils/events'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/index.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/index.test.ts similarity index 98% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/index.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/index.test.ts index 4ea17dfbc0092..8f98b5bfe04b7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/index.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/index.test.ts @@ -9,7 +9,7 @@ import type { RuleExecutorServicesMock } from '@kbn/alerting-plugin/server/mocks import { alertsMock } from '@kbn/alerting-plugin/server/mocks'; import { enrichEvents } from '.'; import { searchEnrichments } from './search_enrichments'; -import { ruleExecutionLogMock } from '../../rule_monitoring/mocks'; +import { ruleExecutionLogMock } from '../../../rule_monitoring/mocks'; import { createAlert } from './__mocks__/alerts'; import { getIsHostRiskScoreAvailable } from './enrichment_by_type/host_risk'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/index.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/index.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/index.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/search_enrichments.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/search_enrichments.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/search_enrichments.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/search_enrichments.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/types.ts similarity index 94% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/types.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/types.ts index 244311fe67df5..1fe7fa9ecfb20 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/types.ts @@ -11,9 +11,9 @@ import type { Filter } from '@kbn/es-query'; import type { BaseFieldsLatest, WrappedFieldsLatest, -} from '../../../../../common/detection_engine/schemas/alerts'; -import type { RuleServices } from '../types'; -import type { IRuleExecutionLogForExecutors } from '../../rule_monitoring'; +} from '../../../../../../common/detection_engine/schemas/alerts'; +import type { RuleServices } from '../../types'; +import type { IRuleExecutionLogForExecutors } from '../../../rule_monitoring'; export type EnrichmentType = estypes.SearchHit; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/utils/events.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/utils/events.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/utils/events.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/utils/events.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/utils/events.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/utils/events.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/utils/events.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/utils/events.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/utils/requests.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/utils/requests.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/utils/requests.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/utils/requests.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/utils/requests.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/utils/requests.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/utils/requests.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/utils/requests.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/utils/transforms.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/utils/transforms.test.ts similarity index 97% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/utils/transforms.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/utils/transforms.test.ts index 0e6de54014c3c..c9eb3a5fea40a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/utils/transforms.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/utils/transforms.test.ts @@ -6,7 +6,7 @@ */ import { applyEnrichmentsToEvents, mergeEnrichments } from './transforms'; -import { ruleExecutionLogMock } from '../../../rule_monitoring/mocks'; +import { ruleExecutionLogMock } from '../../../../rule_monitoring/mocks'; import { createAlert } from '../__mocks__/alerts'; import type { EnrichmentFunction } from '../types'; import { set } from '@kbn/safer-lodash-set'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/utils/transforms.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/utils/transforms.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/enrichments/utils/transforms.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/utils/transforms.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_filter.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_filter.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_filter.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_filter.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_filter.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_filter.ts similarity index 88% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_filter.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_filter.ts index 33d710ed2ffa4..cd1f2d5227a7e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_filter.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_filter.ts @@ -17,12 +17,15 @@ import type { RuleExecutorServices, } from '@kbn/alerting-plugin/server'; import type { Filter } from '@kbn/es-query'; -import { assertUnreachable } from '../../../../common/utility_types'; -import type { IndexPatternArray, RuleQuery } from '../../../../common/detection_engine/rule_schema'; -import type { SavedIdOrUndefined } from '../../../../common/detection_engine/schemas/common/schemas'; -import type { PartialFilter } from '../types'; -import { withSecuritySpan } from '../../../utils/with_security_span'; -import type { ESBoolQuery } from '../../../../common/typed_json'; +import { assertUnreachable } from '../../../../../common/utility_types'; +import type { + IndexPatternArray, + RuleQuery, +} from '../../../../../common/detection_engine/rule_schema'; +import type { SavedIdOrUndefined } from '../../../../../common/detection_engine/schemas/common/schemas'; +import type { PartialFilter } from '../../types'; +import { withSecuritySpan } from '../../../../utils/with_security_span'; +import type { ESBoolQuery } from '../../../../../common/typed_json'; import { getQueryFilter } from './get_query_filter'; interface GetFilterArgs { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_input_output_index.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_input_output_index.test.ts similarity index 99% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_input_output_index.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_input_output_index.test.ts index c898dff30c867..2b024a6932968 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_input_output_index.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_input_output_index.test.ts @@ -10,7 +10,7 @@ import { alertsMock } from '@kbn/alerting-plugin/server/mocks'; import type { MockedLogger } from '@kbn/logging-mocks'; import { loggerMock } from '@kbn/logging-mocks'; -import { DEFAULT_INDEX_KEY, DEFAULT_INDEX_PATTERN } from '../../../../common/constants'; +import { DEFAULT_INDEX_KEY, DEFAULT_INDEX_PATTERN } from '../../../../../common/constants'; import type { GetInputIndex } from './get_input_output_index'; import { getInputIndex, DataViewError } from './get_input_output_index'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_input_output_index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_input_output_index.ts similarity index 97% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_input_output_index.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_input_output_index.ts index b75c351c84f5f..4ec860bc5ae65 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_input_output_index.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_input_output_index.ts @@ -14,8 +14,8 @@ import type { DataViewAttributes } from '@kbn/data-views-plugin/common'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { Logger } from '@kbn/core/server'; -import { DEFAULT_INDEX_KEY, DEFAULT_INDEX_PATTERN } from '../../../../common/constants'; -import { withSecuritySpan } from '../../../utils/with_security_span'; +import { DEFAULT_INDEX_KEY, DEFAULT_INDEX_PATTERN } from '../../../../../common/constants'; +import { withSecuritySpan } from '../../../../utils/with_security_span'; export interface GetInputIndex { index: string[] | null | undefined; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_query_filter.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_query_filter.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_query_filter.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_query_filter.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_query_filter.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_query_filter.ts similarity index 88% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_query_filter.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_query_filter.ts index 15477388839e4..aa6062d321b3a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_query_filter.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_query_filter.ts @@ -8,8 +8,11 @@ import type { Language } from '@kbn/securitysolution-io-ts-alerting-types'; import type { Filter, EsQueryConfig, DataViewBase } from '@kbn/es-query'; import { buildEsQuery } from '@kbn/es-query'; -import type { ESBoolQuery } from '../../../../common/typed_json'; -import type { IndexPatternArray, RuleQuery } from '../../../../common/detection_engine/rule_schema'; +import type { ESBoolQuery } from '../../../../../common/typed_json'; +import type { + IndexPatternArray, + RuleQuery, +} from '../../../../../common/detection_engine/rule_schema'; export const getQueryFilter = ({ query, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/create_field_and_set_tuples.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/large_list_filters/create_field_and_set_tuples.test.ts similarity index 98% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/create_field_and_set_tuples.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/large_list_filters/create_field_and_set_tuples.test.ts index d6b452216be92..593a5fef0bf79 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/create_field_and_set_tuples.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/large_list_filters/create_field_and_set_tuples.test.ts @@ -6,13 +6,13 @@ */ import { createFieldAndSetTuples } from './create_field_and_set_tuples'; -import { sampleDocWithSortId } from '../__mocks__/es_results'; +import { sampleDocWithSortId } from '../../__mocks__/es_results'; import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; import { listMock } from '@kbn/lists-plugin/server/mocks'; import { getSearchListItemResponseMock } from '@kbn/lists-plugin/common/schemas/response/search_list_item_schema.mock'; import type { EntryList } from '@kbn/securitysolution-io-ts-list-types'; -import { ruleExecutionLogMock } from '../../rule_monitoring/mocks'; +import { ruleExecutionLogMock } from '../../../rule_monitoring/mocks'; describe('filterEventsAgainstList', () => { let listClient = listMock.getListClient(); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/create_field_and_set_tuples.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/large_list_filters/create_field_and_set_tuples.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/create_field_and_set_tuples.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/large_list_filters/create_field_and_set_tuples.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/create_set_to_filter_against.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/large_list_filters/create_set_to_filter_against.test.ts similarity index 96% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/create_set_to_filter_against.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/large_list_filters/create_set_to_filter_against.test.ts index d28bc2a39418d..eff2f6f0cd265 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/create_set_to_filter_against.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/large_list_filters/create_set_to_filter_against.test.ts @@ -5,12 +5,12 @@ * 2.0. */ -import { sampleDocWithSortId } from '../__mocks__/es_results'; +import { sampleDocWithSortId } from '../../__mocks__/es_results'; import { listMock } from '@kbn/lists-plugin/server/mocks'; import { getSearchListItemResponseMock } from '@kbn/lists-plugin/common/schemas/response/search_list_item_schema.mock'; import { createSetToFilterAgainst } from './create_set_to_filter_against'; -import { ruleExecutionLogMock } from '../../rule_monitoring/mocks'; +import { ruleExecutionLogMock } from '../../../rule_monitoring/mocks'; describe('createSetToFilterAgainst', () => { let listClient = listMock.getListClient(); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/create_set_to_filter_against.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/large_list_filters/create_set_to_filter_against.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/create_set_to_filter_against.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/large_list_filters/create_set_to_filter_against.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/filter_events.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/large_list_filters/filter_events.test.ts similarity index 98% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/filter_events.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/large_list_filters/filter_events.test.ts index 94fa5f01358be..0f2bd7280642c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/filter_events.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/large_list_filters/filter_events.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { sampleDocWithSortId } from '../__mocks__/es_results'; +import { sampleDocWithSortId } from '../../__mocks__/es_results'; import { listMock } from '@kbn/lists-plugin/server/mocks'; import { getSearchListItemResponseMock } from '@kbn/lists-plugin/common/schemas/response/search_list_item_schema.mock'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/filter_events.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/large_list_filters/filter_events.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/filter_events.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/large_list_filters/filter_events.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/filter_events_against_list.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/large_list_filters/filter_events_against_list.test.ts similarity index 99% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/filter_events_against_list.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/large_list_filters/filter_events_against_list.test.ts index c7c1824d76c69..a50b33a0eee34 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/filter_events_against_list.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/large_list_filters/filter_events_against_list.test.ts @@ -12,8 +12,8 @@ import { getSearchListItemResponseMock } from '@kbn/lists-plugin/common/schemas/ import { listMock } from '@kbn/lists-plugin/server/mocks'; import { filterEventsAgainstList } from './filter_events_against_list'; -import { repeatedHitsWithSortId } from '../__mocks__/es_results'; -import { ruleExecutionLogMock } from '../../rule_monitoring/mocks'; +import { repeatedHitsWithSortId } from '../../__mocks__/es_results'; +import { ruleExecutionLogMock } from '../../../rule_monitoring/mocks'; const someGuids = Array.from({ length: 13 }).map((x) => uuidv4()); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/filter_events_against_list.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/large_list_filters/filter_events_against_list.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/filter_events_against_list.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/large_list_filters/filter_events_against_list.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/large_list_filters/types.ts similarity index 94% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/types.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/large_list_filters/types.ts index f4f8aaf91f969..859000e538fa4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/large_list_filters/types.ts @@ -7,7 +7,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { Type, ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import type { ListClient } from '@kbn/lists-plugin/server'; -import type { IRuleExecutionLogForExecutors } from '../../rule_monitoring'; +import type { IRuleExecutionLogForExecutors } from '../../../rule_monitoring'; export interface FilterEventsAgainstListOptions { listClient: ListClient; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_risk_score_from_mapping.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/mappings/build_risk_score_from_mapping.test.ts similarity index 98% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_risk_score_from_mapping.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/mappings/build_risk_score_from_mapping.test.ts index 5ae4aff3a6687..c144bac9f1181 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_risk_score_from_mapping.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/mappings/build_risk_score_from_mapping.test.ts @@ -6,7 +6,7 @@ */ import type { RiskScore, RiskScoreMapping } from '@kbn/securitysolution-io-ts-alerting-types'; -import { sampleDocRiskScore } from '../__mocks__/es_results'; +import { sampleDocRiskScore } from '../../__mocks__/es_results'; import type { BuildRiskScoreFromMappingReturn } from './build_risk_score_from_mapping'; import { buildRiskScoreFromMapping } from './build_risk_score_from_mapping'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_risk_score_from_mapping.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/mappings/build_risk_score_from_mapping.ts similarity index 95% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_risk_score_from_mapping.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/mappings/build_risk_score_from_mapping.ts index e17c5b941cbb5..95a7034b5310b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_risk_score_from_mapping.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/mappings/build_risk_score_from_mapping.ts @@ -8,8 +8,8 @@ import { get } from 'lodash/fp'; import type { RiskScore, RiskScoreMapping } from '@kbn/securitysolution-io-ts-alerting-types'; -import type { RuleMetadata } from '../../../../../common/detection_engine/rule_schema'; -import type { SignalSource } from '../types'; +import type { RuleMetadata } from '../../../../../../common/detection_engine/rule_schema'; +import type { SignalSource } from '../../types'; export interface BuildRiskScoreFromMappingProps { eventSource: SignalSource; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_rule_name_from_mapping.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/mappings/build_rule_name_from_mapping.test.ts similarity index 92% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_rule_name_from_mapping.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/mappings/build_rule_name_from_mapping.test.ts index 23e5aecc5c553..40bd694ce7d52 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_rule_name_from_mapping.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/mappings/build_rule_name_from_mapping.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { sampleDocNoSortId } from '../__mocks__/es_results'; +import { sampleDocNoSortId } from '../../__mocks__/es_results'; import { buildRuleNameFromMapping } from './build_rule_name_from_mapping'; describe('buildRuleNameFromMapping', () => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_rule_name_from_mapping.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/mappings/build_rule_name_from_mapping.ts similarity index 90% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_rule_name_from_mapping.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/mappings/build_rule_name_from_mapping.ts index 933a330a77098..7bc9c987a4d10 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_rule_name_from_mapping.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/mappings/build_rule_name_from_mapping.ts @@ -12,8 +12,8 @@ import type { RuleMetadata, RuleName, RuleNameOverride, -} from '../../../../../common/detection_engine/rule_schema'; -import type { SignalSource } from '../types'; +} from '../../../../../../common/detection_engine/rule_schema'; +import type { SignalSource } from '../../types'; interface BuildRuleNameFromMappingProps { eventSource: SignalSource; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_severity_from_mapping.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/mappings/build_severity_from_mapping.test.ts similarity index 98% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_severity_from_mapping.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/mappings/build_severity_from_mapping.test.ts index 875da0124b42f..99a82ed03fb9f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_severity_from_mapping.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/mappings/build_severity_from_mapping.test.ts @@ -6,7 +6,7 @@ */ import type { Severity, SeverityMapping } from '@kbn/securitysolution-io-ts-alerting-types'; -import { sampleDocSeverity } from '../__mocks__/es_results'; +import { sampleDocSeverity } from '../../__mocks__/es_results'; import type { BuildSeverityFromMappingReturn } from './build_severity_from_mapping'; import { buildSeverityFromMapping } from './build_severity_from_mapping'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_severity_from_mapping.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/mappings/build_severity_from_mapping.ts similarity index 94% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_severity_from_mapping.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/mappings/build_severity_from_mapping.ts index 53691eeb98814..2a25d00e85f22 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_severity_from_mapping.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/mappings/build_severity_from_mapping.ts @@ -13,9 +13,9 @@ import type { SeverityMappingItem, } from '@kbn/securitysolution-io-ts-alerting-types'; -import type { RuleMetadata } from '../../../../../common/detection_engine/rule_schema'; -import type { SearchTypes } from '../../../../../common/detection_engine/types'; -import type { SignalSource } from '../types'; +import type { RuleMetadata } from '../../../../../../common/detection_engine/rule_schema'; +import type { SearchTypes } from '../../../../../../common/detection_engine/types'; +import type { SignalSource } from '../../types'; export interface BuildSeverityFromMappingProps { eventSource: SignalSource; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/reason_formatter.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/reason_formatter.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/reason_formatter.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/reason_formatter.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/reason_formatters.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/reason_formatters.ts similarity index 99% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/reason_formatters.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/reason_formatters.ts index 28dbc5cc0912b..806edf3765873 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/reason_formatters.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/reason_formatters.ts @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import { getOr } from 'lodash/fp'; -import type { SignalSourceHit } from './types'; +import type { SignalSourceHit } from '../types'; export interface BuildReasonMessageArgs { name: string; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/search_after_bulk_create.test.ts similarity index 98% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/search_after_bulk_create.test.ts index caaf0b8c4cf73..5cdc73d4015b4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/search_after_bulk_create.test.ts @@ -11,26 +11,21 @@ import { repeatedSearchResultsWithNoSortId, sampleDocSearchResultsNoSortIdNoHits, sampleDocWithSortId, -} from './__mocks__/es_results'; +} from '../__mocks__/es_results'; import { searchAfterAndBulkCreate } from './search_after_bulk_create'; import type { RuleExecutorServicesMock } from '@kbn/alerting-plugin/server/mocks'; import { alertsMock } from '@kbn/alerting-plugin/server/mocks'; import { v4 as uuidv4 } from 'uuid'; import { listMock } from '@kbn/lists-plugin/server/mocks'; import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; -import type { BulkCreate, BulkResponse, RuleRangeTuple, WrapHits } from './types'; import type { SearchListItemArraySchema } from '@kbn/securitysolution-io-ts-list-types'; import { getSearchListItemResponseMock } from '@kbn/lists-plugin/common/schemas/response/search_list_item_schema.mock'; -import { getRuleRangeTuples } from './utils'; + import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; -import { getCompleteRuleMock, getQueryRuleParams } from '../rule_schema/mocks'; -import { bulkCreateFactory } from '../rule_types/factories/bulk_create_factory'; -import { wrapHitsFactory } from '../rule_types/factories/wrap_hits_factory'; -import { ruleExecutionLogMock } from '../rule_monitoring/mocks'; -import type { BuildReasonMessage } from './reason_formatters'; -import type { QueryRuleParams } from '../rule_schema'; + import { createPersistenceServicesMock } from '@kbn/rule-registry-plugin/server/utils/create_persistence_rule_type_wrapper.mock'; import type { PersistenceServices } from '@kbn/rule-registry-plugin/server'; +import type { CommonAlertFieldsLatest } from '@kbn/rule-registry-plugin/common/schemas'; import { ALERT_RULE_CATEGORY, ALERT_RULE_CONSUMER, @@ -44,8 +39,15 @@ import { SPACE_IDS, TIMESTAMP, } from '@kbn/rule-data-utils'; -import { SERVER_APP_ID } from '../../../../common/constants'; -import type { CommonAlertFieldsLatest } from '@kbn/rule-registry-plugin/common/schemas'; +import type { BulkCreate, BulkResponse, RuleRangeTuple, WrapHits } from '../types'; +import { getRuleRangeTuples } from './utils'; +import { getCompleteRuleMock, getQueryRuleParams } from '../../rule_schema/mocks'; +import { bulkCreateFactory } from '../factories/bulk_create_factory'; +import { wrapHitsFactory } from '../factories/wrap_hits_factory'; +import { ruleExecutionLogMock } from '../../rule_monitoring/mocks'; +import type { BuildReasonMessage } from './reason_formatters'; +import type { QueryRuleParams } from '../../rule_schema'; +import { SERVER_APP_ID } from '../../../../../common/constants'; describe('searchAfterAndBulkCreate', () => { let mockService: RuleExecutorServicesMock; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/search_after_bulk_create.ts similarity index 97% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/search_after_bulk_create.ts index e57e6603cb067..78bead8d70ff6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/search_after_bulk_create.ts @@ -8,7 +8,7 @@ import { identity } from 'lodash'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { singleSearchAfter } from './single_search_after'; -import { filterEventsAgainstList } from './filters/filter_events_against_list'; +import { filterEventsAgainstList } from './large_list_filters/filter_events_against_list'; import { sendAlertTelemetryEvents } from './send_telemetry_events'; import { createSearchAfterReturnType, @@ -20,8 +20,8 @@ import { getSafeSortIds, addToSearchAfterReturn, } from './utils'; -import type { SearchAfterAndBulkCreateParams, SearchAfterAndBulkCreateReturnType } from './types'; -import { withSecuritySpan } from '../../../utils/with_security_span'; +import type { SearchAfterAndBulkCreateParams, SearchAfterAndBulkCreateReturnType } from '../types'; +import { withSecuritySpan } from '../../../../utils/with_security_span'; import { createEnrichEventsFunction } from './enrichments'; // search_after through documents and re-index using bulk endpoint. diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/send_telemetry_events.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/send_telemetry_events.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/send_telemetry_events.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/send_telemetry_events.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/send_telemetry_events.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/send_telemetry_events.ts similarity index 89% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/send_telemetry_events.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/send_telemetry_events.ts index 65742d5145110..a4687fa953dfe 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/send_telemetry_events.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/send_telemetry_events.ts @@ -5,10 +5,10 @@ * 2.0. */ -import type { ITelemetryEventsSender } from '../../telemetry/sender'; -import type { TelemetryEvent } from '../../telemetry/types'; -import type { IRuleExecutionLogForExecutors } from '../rule_monitoring'; -import type { SignalSource, SignalSourceHit } from './types'; +import type { ITelemetryEventsSender } from '../../../telemetry/sender'; +import type { TelemetryEvent } from '../../../telemetry/types'; +import type { IRuleExecutionLogForExecutors } from '../../rule_monitoring'; +import type { SignalSource, SignalSourceHit } from '../types'; interface SearchResultSource { _source: SignalSource; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/siem_rule_action_groups.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/siem_rule_action_groups.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/siem_rule_action_groups.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/siem_rule_action_groups.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/single_search_after.test.ts similarity index 98% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/single_search_after.test.ts index 7b4e1b8ecf00a..136951ebb0b4a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/single_search_after.test.ts @@ -8,12 +8,12 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { sampleDocSearchResultsNoSortId, sampleDocSearchResultsWithSortId, -} from './__mocks__/es_results'; +} from '../__mocks__/es_results'; import { singleSearchAfter } from './single_search_after'; import type { RuleExecutorServicesMock } from '@kbn/alerting-plugin/server/mocks'; import { alertsMock } from '@kbn/alerting-plugin/server/mocks'; import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; -import { ruleExecutionLogMock } from '../rule_monitoring/mocks'; +import { ruleExecutionLogMock } from '../../rule_monitoring/mocks'; import { buildEventsSearchQuery } from './build_events_query'; jest.mock('./build_events_query'); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/single_search_after.ts similarity index 94% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/single_search_after.ts index 3be6f3ad30e49..eea89c880e04f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/single_search_after.ts @@ -11,12 +11,12 @@ import type { AlertInstanceState, RuleExecutorServices, } from '@kbn/alerting-plugin/server'; -import type { SignalSearchResponse, SignalSource, OverrideBodyQuery } from './types'; +import type { SignalSearchResponse, SignalSource, OverrideBodyQuery } from '../types'; import { buildEventsSearchQuery } from './build_events_query'; import { createErrorsFromShard, makeFloatString } from './utils'; -import type { TimestampOverride } from '../../../../common/detection_engine/rule_schema'; -import { withSecuritySpan } from '../../../utils/with_security_span'; -import type { IRuleExecutionLogForExecutors } from '../rule_monitoring'; +import type { TimestampOverride } from '../../../../../common/detection_engine/rule_schema'; +import { withSecuritySpan } from '../../../../utils/with_security_span'; +import type { IRuleExecutionLogForExecutors } from '../../rule_monitoring'; interface SingleSearchAfterParams { aggregations?: Record; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/README.md b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/README.md similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/README.md rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/README.md diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/index.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/index.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/index.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/strategies/get_strategy.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/strategies/get_strategy.ts similarity index 86% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/strategies/get_strategy.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/strategies/get_strategy.ts index 636a0bbb3de6f..ab339fdd6f68f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/strategies/get_strategy.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/strategies/get_strategy.ts @@ -5,8 +5,8 @@ * 2.0. */ -import { assertUnreachable } from '../../../../../../common/utility_types'; -import type { ConfigType } from '../../../../../config'; +import { assertUnreachable } from '../../../../../../../common/utility_types'; +import type { ConfigType } from '../../../../../../config'; import type { MergeStrategyFunction } from '../types'; import { mergeAllFieldsWithSource } from './merge_all_fields_with_source'; import { mergeMissingFieldsWithSource } from './merge_missing_fields_with_source'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/strategies/index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/strategies/index.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/strategies/index.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/strategies/index.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/strategies/merge_all_fields_with_source.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/strategies/merge_all_fields_with_source.test.ts similarity index 99% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/strategies/merge_all_fields_with_source.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/strategies/merge_all_fields_with_source.test.ts index 74445d4be790a..5aac1418cf45d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/strategies/merge_all_fields_with_source.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/strategies/merge_all_fields_with_source.test.ts @@ -6,8 +6,8 @@ */ import { mergeAllFieldsWithSource } from './merge_all_fields_with_source'; -import type { SignalSourceHit } from '../../types'; -import { emptyEsResult } from '../../__mocks__/empty_signal_source_hit'; +import type { SignalSourceHit } from '../../../types'; +import { emptyEsResult } from '../../../__mocks__/empty_signal_source_hit'; /** * See ../README.md for the nomenclature of any notes within tests below diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/strategies/merge_all_fields_with_source.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/strategies/merge_all_fields_with_source.ts similarity index 98% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/strategies/merge_all_fields_with_source.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/strategies/merge_all_fields_with_source.ts index e3c7f8f5ee50e..b9193f952fd18 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/strategies/merge_all_fields_with_source.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/strategies/merge_all_fields_with_source.ts @@ -7,7 +7,7 @@ import { get } from 'lodash/fp'; import { set } from '@kbn/safer-lodash-set/fp'; -import type { SignalSource } from '../../types'; +import type { SignalSource } from '../../../types'; import { filterFieldEntries } from '../utils/filter_field_entries'; import type { FieldsType, MergeStrategyFunction } from '../types'; import { isObjectLikeOrArrayOfObjectLikes } from '../utils/is_objectlike_or_array_of_objectlikes'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/strategies/merge_missing_fields_with_source.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/strategies/merge_missing_fields_with_source.test.ts similarity index 99% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/strategies/merge_missing_fields_with_source.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/strategies/merge_missing_fields_with_source.test.ts index f5863533ea283..911df7400ec63 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/strategies/merge_missing_fields_with_source.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/strategies/merge_missing_fields_with_source.test.ts @@ -6,8 +6,8 @@ */ import { performance } from 'perf_hooks'; import { mergeMissingFieldsWithSource } from './merge_missing_fields_with_source'; -import type { SignalSourceHit } from '../../types'; -import { emptyEsResult } from '../../__mocks__/empty_signal_source_hit'; +import type { SignalSourceHit } from '../../../types'; +import { emptyEsResult } from '../../../__mocks__/empty_signal_source_hit'; /** * See ../README.md for the nomenclature of any notes within tests below diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/strategies/merge_missing_fields_with_source.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/strategies/merge_missing_fields_with_source.ts similarity index 98% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/strategies/merge_missing_fields_with_source.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/strategies/merge_missing_fields_with_source.ts index c20f6b55301bd..3efe1a7925d9b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/strategies/merge_missing_fields_with_source.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/strategies/merge_missing_fields_with_source.ts @@ -7,7 +7,7 @@ import { get } from 'lodash/fp'; import { set } from '@kbn/safer-lodash-set'; -import type { SignalSource } from '../../types'; +import type { SignalSource } from '../../../types'; import { filterFieldEntries } from '../utils/filter_field_entries'; import type { FieldsType, MergeStrategyFunction } from '../types'; import { recursiveUnboxingFields } from '../utils/recursive_unboxing_fields'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/strategies/merge_no_fields.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/strategies/merge_no_fields.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/strategies/merge_no_fields.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/strategies/merge_no_fields.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/types.ts similarity index 93% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/types.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/types.ts index 56ca8fb460374..68d0c2f047727 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { SignalSourceHit } from '../types'; +import type { SignalSourceHit } from '../../types'; /** * A bit stricter typing since the default fields type is an "any" diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/array_in_path_exists.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/array_in_path_exists.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/array_in_path_exists.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/array_in_path_exists.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/array_in_path_exists.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/array_in_path_exists.ts similarity index 94% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/array_in_path_exists.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/array_in_path_exists.ts index cc891b0f609a1..e13dd0ffdd5f1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/array_in_path_exists.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/array_in_path_exists.ts @@ -6,7 +6,7 @@ */ import { get } from 'lodash/fp'; -import type { SignalSource } from '../../types'; +import type { SignalSource } from '../../../types'; /** * Returns true if an array within the path exists anywhere. diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/filter_field_entries.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/filter_field_entries.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/filter_field_entries.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/filter_field_entries.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/filter_field_entries.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/filter_field_entries.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/filter_field_entries.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/filter_field_entries.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/index.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/index.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/index.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_array_of_primitives.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_array_of_primitives.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_array_of_primitives.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_array_of_primitives.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_array_of_primitives.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_array_of_primitives.ts similarity index 90% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_array_of_primitives.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_array_of_primitives.ts index 80916659b8a12..765eb66354dd2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_array_of_primitives.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_array_of_primitives.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { SearchTypes } from '../../../../../../common/detection_engine/types'; +import type { SearchTypes } from '../../../../../../../common/detection_engine/types'; import { isPrimitive } from './is_primitive'; /** diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_eql_bug_77152.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_eql_bug_77152.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_eql_bug_77152.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_eql_bug_77152.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_eql_bug_77152.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_eql_bug_77152.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_eql_bug_77152.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_eql_bug_77152.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_ignored.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_ignored.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_ignored.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_ignored.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_ignored.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_ignored.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_ignored.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_ignored.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_invalid_key.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_invalid_key.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_invalid_key.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_invalid_key.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_invalid_key.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_invalid_key.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_invalid_key.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_invalid_key.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_multifield.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_multifield.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_multifield.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_multifield.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_multifield.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_multifield.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_multifield.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_multifield.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_nested_object.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_nested_object.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_nested_object.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_nested_object.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_nested_object.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_nested_object.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_nested_object.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_nested_object.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_objectlike_or_array_of_objectlikes.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_objectlike_or_array_of_objectlikes.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_objectlike_or_array_of_objectlikes.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_objectlike_or_array_of_objectlikes.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_objectlike_or_array_of_objectlikes.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_objectlike_or_array_of_objectlikes.ts similarity index 91% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_objectlike_or_array_of_objectlikes.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_objectlike_or_array_of_objectlikes.ts index 38c3db1d8fd24..39d215d4c5062 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_objectlike_or_array_of_objectlikes.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_objectlike_or_array_of_objectlikes.ts @@ -6,7 +6,7 @@ */ import { isObjectLike } from 'lodash/fp'; -import type { SearchTypes } from '../../../../../../common/detection_engine/types'; +import type { SearchTypes } from '../../../../../../../common/detection_engine/types'; /** * Returns true if at least one element is an object, otherwise false if they all are not objects diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_primitive.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_primitive.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_primitive.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_primitive.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_primitive.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_primitive.ts similarity index 85% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_primitive.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_primitive.ts index fd9997ba66a55..bd053036176cc 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_primitive.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_primitive.ts @@ -6,7 +6,7 @@ */ import { isObjectLike } from 'lodash/fp'; -import type { SearchTypes } from '../../../../../../common/detection_engine/types'; +import type { SearchTypes } from '../../../../../../../common/detection_engine/types'; /** * Returns true if it is a primitive type, otherwise false diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_type_object.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_type_object.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_type_object.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_type_object.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_type_object.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_type_object.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/is_type_object.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/is_type_object.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/recursive_unboxing_fields.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/recursive_unboxing_fields.test.ts similarity index 99% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/recursive_unboxing_fields.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/recursive_unboxing_fields.test.ts index 2d2febf9901d3..c606c87812c16 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/recursive_unboxing_fields.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/recursive_unboxing_fields.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { SearchTypes } from '../../../../../../common/detection_engine/types'; +import type { SearchTypes } from '../../../../../../../common/detection_engine/types'; import { recursiveUnboxingFields } from './recursive_unboxing_fields'; import type { FieldsType } from '../types'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/recursive_unboxing_fields.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/recursive_unboxing_fields.ts similarity index 96% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/recursive_unboxing_fields.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/recursive_unboxing_fields.ts index 8bb490c2d62df..55f7e30c256a3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/source_fields_merging/utils/recursive_unboxing_fields.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/source_fields_merging/utils/recursive_unboxing_fields.ts @@ -7,7 +7,7 @@ import { get } from 'lodash/fp'; import { set } from '@kbn/safer-lodash-set/fp'; -import type { SearchTypes } from '../../../../../../common/detection_engine/types'; +import type { SearchTypes } from '../../../../../../../common/detection_engine/types'; import type { FieldsType } from '../types'; /** diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/utils.test.ts similarity index 98% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/utils.test.ts index d80ed256a0de0..e39573562d277 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/utils.test.ts @@ -14,8 +14,8 @@ import type { RuleExecutorServicesMock } from '@kbn/alerting-plugin/server/mocks import { alertsMock } from '@kbn/alerting-plugin/server/mocks'; import { listMock } from '@kbn/lists-plugin/server/mocks'; import type { ExceptionListClient } from '@kbn/lists-plugin/server'; -import { RuleExecutionStatus } from '../../../../common/detection_engine/rule_monitoring'; -import { getListArrayMock } from '../../../../common/detection_engine/schemas/types/lists.mock'; +import { RuleExecutionStatus } from '../../../../../common/detection_engine/rule_monitoring'; +import { getListArrayMock } from '../../../../../common/detection_engine/schemas/types/lists.mock'; import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; moment.suppressDeprecationWarnings = true; @@ -37,7 +37,6 @@ import { createSearchAfterReturnType, mergeReturns, lastValidDate, - calculateThresholdSignalUuid, buildChunkedOrFilter, getValidDateFromDoc, calculateTotal, @@ -47,7 +46,7 @@ import { addToSearchAfterReturn, getUnprocessedExceptionsWarnings, } from './utils'; -import type { BulkResponseErrorAggregation, SearchAfterAndBulkCreateReturnType } from './types'; +import type { BulkResponseErrorAggregation, SearchAfterAndBulkCreateReturnType } from '../types'; import { sampleBulkResponse, sampleEmptyBulkResponse, @@ -61,11 +60,11 @@ import { sampleDocNoSortId, sampleAlertDocNoSortIdWithTimestamp, sampleAlertDocAADNoSortIdWithTimestamp, -} from './__mocks__/es_results'; -import type { ShardError } from '../../types'; -import { ruleExecutionLogMock } from '../rule_monitoring/mocks'; -import type { GenericBulkCreateResponse } from '../rule_types/factories'; -import type { BaseFieldsLatest } from '../../../../common/detection_engine/schemas/alerts'; +} from '../__mocks__/es_results'; +import type { ShardError } from '../../../types'; +import { ruleExecutionLogMock } from '../../rule_monitoring/mocks'; +import type { GenericBulkCreateResponse } from '../factories'; +import type { BaseFieldsLatest } from '../../../../../common/detection_engine/schemas/alerts'; describe('utils', () => { const anchor = '2020-01-01T06:06:06.666Z'; @@ -1508,20 +1507,6 @@ describe('utils', () => { }); }); - describe('calculateThresholdSignalUuid', () => { - it('should generate a uuid without key', () => { - const startedAt = new Date('2020-12-17T16:27:00Z'); - const signalUuid = calculateThresholdSignalUuid('abcd', startedAt, ['agent.name']); - expect(signalUuid).toEqual('a4832768-a379-583a-b1a2-e2ce2ad9e6e9'); - }); - - it('should generate a uuid with key', () => { - const startedAt = new Date('2019-11-18T13:32:00Z'); - const signalUuid = calculateThresholdSignalUuid('abcd', startedAt, ['host.ip'], '1.2.3.4'); - expect(signalUuid).toEqual('ee8870dc-45ff-5e6c-a2f9-80886651ce03'); - }); - }); - describe('buildChunkedOrFilter', () => { test('should return undefined if no values are provided', () => { const filter = buildChunkedOrFilter('field.name', []); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/utils.ts similarity index 95% rename from x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/utils.ts index 1899b02811194..711db3dc1e601 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/utils.ts @@ -7,7 +7,6 @@ import { createHash } from 'crypto'; import { chunk, get, invert, isEmpty, partition } from 'lodash'; import moment from 'moment'; -import { v5 as uuidv5 } from 'uuid'; import dateMath from '@kbn/datemath'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; @@ -31,9 +30,9 @@ import type { } from '@kbn/alerting-plugin/server'; import { parseDuration } from '@kbn/alerting-plugin/server'; import type { ExceptionListClient, ListClient, ListPluginSetup } from '@kbn/lists-plugin/server'; -import type { TimestampOverride } from '../../../../common/detection_engine/rule_schema'; -import type { Privilege } from '../../../../common/detection_engine/schemas/common'; -import { RuleExecutionStatus } from '../../../../common/detection_engine/rule_monitoring'; +import type { TimestampOverride } from '../../../../../common/detection_engine/rule_schema'; +import type { Privilege } from '../../../../../common/detection_engine/schemas/common'; +import { RuleExecutionStatus } from '../../../../../common/detection_engine/rule_monitoring'; import type { BulkResponseErrorAggregation, SignalHit, @@ -46,8 +45,8 @@ import type { SignalSourceHit, SimpleHit, WrappedEventHit, -} from './types'; -import type { ShardError } from '../../types'; +} from '../types'; +import type { ShardError } from '../../../types'; import type { EqlRuleParams, MachineLearningRuleParams, @@ -55,16 +54,16 @@ import type { RuleParams, ThreatRuleParams, ThresholdRuleParams, -} from '../rule_schema'; -import type { BaseHit, SearchTypes } from '../../../../common/detection_engine/types'; -import type { IRuleExecutionLogForExecutors } from '../rule_monitoring'; -import { withSecuritySpan } from '../../../utils/with_security_span'; +} from '../../rule_schema'; +import type { BaseHit, SearchTypes } from '../../../../../common/detection_engine/types'; +import type { IRuleExecutionLogForExecutors } from '../../rule_monitoring'; +import { withSecuritySpan } from '../../../../utils/with_security_span'; import type { BaseFieldsLatest, DetectionAlert, -} from '../../../../common/detection_engine/schemas/alerts'; -import { ENABLE_CCS_READ_WARNING_SETTING } from '../../../../common/constants'; -import type { GenericBulkCreateResponse } from '../rule_types/factories'; +} from '../../../../../common/detection_engine/schemas/alerts'; +import { ENABLE_CCS_READ_WARNING_SETTING } from '../../../../../common/constants'; +import type { GenericBulkCreateResponse } from '../factories'; export const MAX_RULE_GAP_RATIO = 4; @@ -833,40 +832,6 @@ export const calculateTotal = ( return prevTotalHits + nextTotalHits; }; -export const calculateThresholdSignalUuid = ( - ruleId: string, - startedAt: Date, - thresholdFields: string[], - key?: string -): string => { - // used to generate stable Threshold Signals ID when run with the same params - const NAMESPACE_ID = '0684ec03-7201-4ee0-8ee0-3a3f6b2479b2'; - - const startedAtString = startedAt.toISOString(); - const keyString = key ?? ''; - const baseString = `${ruleId}${startedAtString}${thresholdFields.join(',')}${keyString}`; - - return uuidv5(baseString, NAMESPACE_ID); -}; - -export const getThresholdTermsHash = ( - terms: Array<{ - field: string; - value: string; - }> -): string => { - return createHash('sha256') - .update( - terms - .sort((term1, term2) => (term1.field > term2.field ? 1 : -1)) - .map((term) => { - return `${term.field}:${term.value}`; - }) - .join(',') - ) - .digest('hex'); -}; - export const isEqlParams = (params: RuleParams): params is EqlRuleParams => params.type === 'eql'; export const isThresholdParams = (params: RuleParams): params is ThresholdRuleParams => params.type === 'threshold'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/threshold_signal_history.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/threshold_signal_history.mock.ts deleted file mode 100644 index 4c43bd9ee1bde..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/threshold_signal_history.mock.ts +++ /dev/null @@ -1,28 +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 type { ThresholdSignalHistory } from '../types'; -import { getThresholdTermsHash } from '../utils'; - -export const sampleThresholdSignalHistory = (): ThresholdSignalHistory => { - const terms = [ - { - field: 'source.ip', - value: '127.0.0.1', - }, - { - field: 'host.name', - value: 'garden-gnomes', - }, - ]; - return { - [`${getThresholdTermsHash(terms)}`]: { - terms, - lastSignalTimestamp: new Date('2020-12-17T16:28:00Z').getTime(), - }, - }; -}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/utils.ts deleted file mode 100644 index 1e1ecbc12b444..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/utils.ts +++ /dev/null @@ -1,15 +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 type { - ThresholdNormalized, - ThresholdWithCardinality, -} from '../../../../../common/detection_engine/rule_schema'; - -export const shouldFilterByCardinality = ( - threshold: ThresholdNormalized -): threshold is ThresholdWithCardinality => !!threshold.cardinality?.length; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts deleted file mode 100644 index 877239af49a9a..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts +++ /dev/null @@ -1,350 +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 type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import type moment from 'moment'; -import type { ESSearchResponse } from '@kbn/es-types'; -import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; -import type { - RuleTypeState, - AlertInstanceState, - AlertInstanceContext, - RuleExecutorOptions as AlertingRuleExecutorOptions, - RuleExecutorServices, -} from '@kbn/alerting-plugin/server'; -import type { ListClient } from '@kbn/lists-plugin/server'; -import type { EcsFieldMap } from '@kbn/rule-registry-plugin/common/assets/field_maps/ecs_field_map'; -import type { TypeOfFieldMap } from '@kbn/rule-registry-plugin/common/field_map'; -import type { Status } from '../../../../common/detection_engine/schemas/common/schemas'; -import type { - BaseHit, - RuleAlertAction, - SearchTypes, - EqlSequence, -} from '../../../../common/detection_engine/types'; -import type { ITelemetryEventsSender } from '../../telemetry/sender'; -import type { RuleParams, UnifiedQueryRuleParams } from '../rule_schema'; -import type { GenericBulkCreateResponse } from '../rule_types/factories'; -import type { BuildReasonMessage } from './reason_formatters'; -import type { - BaseFieldsLatest, - DetectionAlert, - WrappedFieldsLatest, -} from '../../../../common/detection_engine/schemas/alerts'; -import type { IRuleExecutionLogForExecutors } from '../rule_monitoring'; -import type { buildGroupByFieldAggregation } from './alert_suppression/build_group_by_field_aggregation'; -import type { RuleResponse } from '../../../../common/detection_engine/rule_schema'; -import type { EnrichEvents } from './enrichments/types'; -import type { BucketHistory } from './alert_suppression/group_and_bulk_create'; -import type { RunOpts } from '../rule_types/types'; - -export interface ThresholdResult { - terms?: Array<{ - field: string; - value: string; - }>; - cardinality?: Array<{ - field: string; - value: number; - }>; - count: number; - from: string; -} - -export interface ThresholdSignalHistoryRecord { - terms: Array<{ - field?: string; - value: SearchTypes; - }>; - lastSignalTimestamp: number; -} - -export interface ThresholdSignalHistory { - [hash: string]: ThresholdSignalHistoryRecord; -} - -export interface RuleRangeTuple { - to: moment.Moment; - from: moment.Moment; - maxSignals: number; -} - -/** - * SignalSource is being used as both a type for documents that match detection engine queries as well as - * for queries that could be on top of signals. In cases where it is matched against detection engine queries, - * '@timestamp' might not be there since it is not required and we have timestamp override capabilities. Also - * the signal addition object, "signal?: {" will not be there unless it's a conflicting field when we are running - * queries on events. - * - * For cases where we are running queries against signals (signals on signals) "@timestamp" should always be there - * and the "signal?: {" sub-object should always be there. - */ -export interface SignalSource { - [key: string]: SearchTypes; - '@timestamp'?: string; - signal?: { - /** - * "parent" is deprecated: new signals should populate "parents" instead. Both are optional - * until all signals with parent are gone and we can safely remove it. - * @deprecated Use parents instead - */ - parent?: Ancestor; - parents?: Ancestor[]; - ancestors: Ancestor[]; - group?: { - id: string; - index?: number; - }; - rule: { - id: string; - description?: string; - false_positives?: string[]; - immutable?: boolean; - }; - /** signal.depth was introduced in 7.10 and pre-7.10 signals do not have it. */ - depth?: number; - original_time?: string; - /** signal.reason was introduced in 7.15 and pre-7.15 signals do not have it. */ - reason?: string; - status?: string; - threshold_result?: ThresholdResult; - }; - kibana?: SearchTypes; -} - -export interface BulkItem { - create?: { - _index: string; - _type?: string; - _id: string; - _version: number; - result?: string; - _shards?: { - total: number; - successful: number; - failed: number; - }; - _seq_no?: number; - _primary_term?: number; - status: number; - error?: { - type: string; - reason: string; - index_uuid?: string; - shard: string; - index: string; - }; - }; -} - -export interface BulkResponse { - took: number; - errors: boolean; - items: BulkItem[]; -} - -export interface GetResponse { - _index: string; - _type: string; - _id: string; - _version: number; - _seq_no: number; - _primary_term: number; - found: boolean; - _source: SearchTypes; -} - -export type EventHit = Exclude, '@timestamp'> & { - '@timestamp': string; - [key: string]: SearchTypes; -}; -export type WrappedEventHit = BaseHit; - -export type SignalSearchResponse< - TAggregations = Record -> = estypes.SearchResponse; -export type SignalSourceHit = estypes.SearchHit; -export type AlertSourceHit = estypes.SearchHit; -export type WrappedSignalHit = BaseHit; -export type BaseSignalHit = estypes.SearchHit; - -export type RuleExecutorOptions = AlertingRuleExecutorOptions< - RuleParams, - RuleTypeState, - AlertInstanceState, - AlertInstanceContext ->; - -export interface Ancestor { - rule?: string; - id: string; - type: string; - index: string; - depth: number; -} - -export interface Signal { - _meta?: { - version: number; - }; - rule: RuleResponse; - /** - * @deprecated Use "parents" instead of "parent" - */ - parent?: Ancestor; - parents: Ancestor[]; - ancestors: Ancestor[]; - group?: { - id: string; - index?: number; - }; - original_time?: string; - original_event?: SearchTypes; - reason?: string; - status: Status; - threshold_result?: ThresholdResult; - original_signal?: SearchTypes; - depth: number; -} - -export interface SignalHit { - '@timestamp': string; - event: object; - signal: Signal; - [key: string]: SearchTypes; -} - -export interface AlertAttributes { - actions: RuleAlertAction[]; - alertTypeId: string; - enabled: boolean; - name: string; - tags: string[]; - createdBy: string; - createdAt: string; - updatedBy: string; - schedule: { - interval: string; - }; - throttle: string; - params: T; -} - -export type BulkResponseErrorAggregation = Record; - -export type SignalsEnrichment = (signals: SignalSourceHit[]) => Promise; - -export type BulkCreate = ( - docs: Array>, - maxAlerts?: number, - enrichEvents?: EnrichEvents -) => Promise>; - -export type SimpleHit = BaseHit<{ '@timestamp'?: string }>; - -export type WrapHits = ( - hits: Array>, - buildReasonMessage: BuildReasonMessage -) => Array>; - -export type WrapSequences = ( - sequences: Array>, - buildReasonMessage: BuildReasonMessage -) => Array>; - -export type RuleServices = RuleExecutorServices< - AlertInstanceState, - AlertInstanceContext, - 'default' ->; -export interface SearchAfterAndBulkCreateParams { - tuple: { - to: moment.Moment; - from: moment.Moment; - maxSignals: number; - }; - services: RuleServices; - listClient: ListClient; - exceptionsList: ExceptionListItemSchema[]; - ruleExecutionLogger: IRuleExecutionLogForExecutors; - eventsTelemetry: ITelemetryEventsSender | undefined; - inputIndexPattern: string[]; - pageSize: number; - filter: estypes.QueryDslQueryContainer; - buildReasonMessage: BuildReasonMessage; - enrichment?: SignalsEnrichment; - bulkCreate: BulkCreate; - wrapHits: WrapHits; - trackTotalHits?: boolean; - sortOrder?: estypes.SortOrder; - runtimeMappings: estypes.MappingRuntimeFields | undefined; - primaryTimestamp: string; - secondaryTimestamp?: string; -} - -export interface GroupAndBulkCreateParams { - runOpts: RunOpts; - services: RuleServices; - spaceId: string; - filter: estypes.QueryDslQueryContainer; - buildReasonMessage: BuildReasonMessage; - bucketHistory?: BucketHistory[]; - groupByFields: string[]; -} - -export interface SearchAfterAndBulkCreateReturnType { - success: boolean; - warning: boolean; - searchAfterTimes: string[]; - enrichmentTimes: string[]; - bulkCreateTimes: string[]; - lastLookBackDate: Date | null | undefined; - createdSignalsCount: number; - createdSignals: unknown[]; - errors: string[]; - warningMessages: string[]; -} - -export interface GroupAndBulkCreateReturnType extends SearchAfterAndBulkCreateReturnType { - state: { - suppressionGroupHistory: BucketHistory[]; - }; -} - -export interface MultiAggBucket { - cardinality?: Array<{ - field: string; - value: number; - }>; - terms: Array<{ - field: string; - value: string; - }>; - docCount: number; - maxTimestamp: string; - minTimestamp: string; -} - -export interface ThresholdAlertState extends RuleTypeState { - initialized: boolean; - signalHistory: ThresholdSignalHistory; -} - -export type EventGroupingMultiBucketAggregationResult = ESSearchResponse< - SignalSource, - { - body: { - aggregations: ReturnType; - }; - } ->; - -// the new fields can be added later if needed -export interface OverrideBodyQuery { - _source?: estypes.SearchSourceConfig; - fields?: estypes.Fields; -} diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/filterlists/endpoint_alerts.ts b/x-pack/plugins/security_solution/server/lib/telemetry/filterlists/endpoint_alerts.ts index 1174ad013dec4..ff5f973df53e2 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/filterlists/endpoint_alerts.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/filterlists/endpoint_alerts.ts @@ -38,6 +38,8 @@ const baseAllowlistFields: AllowlistFields = { integrity_level_name: true, security_attributes: true, }, + effective_parent: true, + device: true, }, thread: true, working_directory: true, @@ -54,9 +56,7 @@ const allowlistBaseEventFields: AllowlistFields = { malware_signature: true, pe: true, Ext: { - device: { - volume_device_type: true, - }, + device: true, load_index: true, relative_file_creation_time: true, relative_file_name_modify_time: true, @@ -114,6 +114,8 @@ const allowlistBaseEventFields: AllowlistFields = { id: true, }, Persistence: true, + /* eslint-disable @typescript-eslint/naming-convention */ + Effective_process: true, }; // Allow list for the data we include in the events. True means that it is deep-cloned diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/sender.test.ts b/x-pack/plugins/security_solution/server/lib/telemetry/sender.test.ts index 0fa1e1512e763..958355aaf477a 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/sender.test.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/sender.test.ts @@ -94,11 +94,16 @@ describe('TelemetryEventsSender', () => { dll: { Ext: { device: { - volume_device_type: 'Disk File System', + bus_type: 'FileBackedVirtual', + dos_name: 'D:', + file_system_type: 'CDFS', + nt_name: 'CdRom0', + product_id: 'Virtual DVD-ROM', + vendor_id: 'Msft', + volume_device_type: 'CD-ROM File System', }, - load_index: 1, - relative_file_creation_time: 48628704.4029488, - relative_file_name_modify_time: 48628704.4029488, + relative_file_creation_time: 35588490.2737149, + relative_file_name_modify_time: 35588490.2424634, }, }, file: { @@ -177,6 +182,21 @@ describe('TelemetryEventsSender', () => { protection: 'PsProtectedSignerAntimalware-Light', relative_file_creation_time: 48628704.4029488, relative_file_name_modify_time: 48628704.4029488, + device: { + bus_type: 'FileBackedVirtual', + volume_device_type: 'CD-ROM File System', + dos_name: 'D:', + product_id: 'Virtual DVD-ROM', + vendor_id: 'Msft', + nt_name: 'CdRom0', + file_system_type: 'CDFS', + }, + effective_parent: { + name: 'file.exe', + pid: 6792, + entity_id: 'some_entity_id', + executable: 'DeviceHarddiskVolume3WindowsSystem32file.exe', + }, session_info: { logon_type: 'Interactive', client_address: '127.0.0.1', @@ -271,16 +291,6 @@ describe('TelemetryEventsSender', () => { ruleset: 'Z', version: '100', }, - dll: { - Ext: { - device: { - volume_device_type: 'Disk File System', - }, - load_index: 1, - relative_file_creation_time: 48628704.4029488, - relative_file_name_modify_time: 48628704.4029488, - }, - }, file: { extension: '.exe', size: 3, @@ -349,6 +359,21 @@ describe('TelemetryEventsSender', () => { protection: 'PsProtectedSignerAntimalware-Light', relative_file_creation_time: 48628704.4029488, relative_file_name_modify_time: 48628704.4029488, + device: { + bus_type: 'FileBackedVirtual', + volume_device_type: 'CD-ROM File System', + dos_name: 'D:', + product_id: 'Virtual DVD-ROM', + vendor_id: 'Msft', + nt_name: 'CdRom0', + file_system_type: 'CDFS', + }, + effective_parent: { + name: 'file.exe', + pid: 6792, + entity_id: 'some_entity_id', + executable: 'DeviceHarddiskVolume3WindowsSystem32file.exe', + }, session_info: { logon_type: 'Interactive', client_address: '127.0.0.1', @@ -375,6 +400,21 @@ describe('TelemetryEventsSender', () => { runatload: true, args: ['foo', 'bar'], }, + dll: { + Ext: { + relative_file_creation_time: 35588490.2737149, + relative_file_name_modify_time: 35588490.2424634, + device: { + bus_type: 'FileBackedVirtual', + volume_device_type: 'CD-ROM File System', + dos_name: 'D:', + product_id: 'Virtual DVD-ROM', + vendor_id: 'Msft', + nt_name: 'CdRom0', + file_system_type: 'CDFS', + }, + }, + }, }, ]); }); diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index db7a32670727b..cbc595a22bbf0 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -21,10 +21,10 @@ import type { Logger } from '@kbn/core/server'; import { SavedObjectsClient } from '@kbn/core/server'; import type { UsageCounter } from '@kbn/usage-collection-plugin/server'; -import { ECS_COMPONENT_TEMPLATE_NAME } from '@kbn/rule-registry-plugin/common/assets'; -import type { FieldMap } from '@kbn/rule-registry-plugin/common/field_map'; +import { ECS_COMPONENT_TEMPLATE_NAME } from '@kbn/alerting-plugin/server'; +import { mappingFromFieldMap } from '@kbn/alerting-plugin/common'; +import type { FieldMap } from '@kbn/alerts-as-data-utils'; import { technicalRuleFieldMap } from '@kbn/rule-registry-plugin/common/assets/field_maps/technical_rule_field_map'; -import { mappingFromFieldMap } from '@kbn/rule-registry-plugin/common/mapping_from_field_map'; import type { IRuleDataClient } from '@kbn/rule-registry-plugin/server'; import { Dataset } from '@kbn/rule-registry-plugin/server'; import type { ListPluginSetup } from '@kbn/lists-plugin/server'; @@ -220,6 +220,7 @@ export class Plugin implements ISecuritySolutionPlugin { Object.entries(aadFieldConversion).forEach(([key, value]) => { aliasesFieldMap[key] = { type: 'alias', + required: false, path: value, }; }); @@ -509,6 +510,7 @@ export class Plugin implements ISecuritySolutionPlugin { registerListsServerExtension: this.lists?.registerExtension, featureUsageService, experimentalFeatures: config.experimentalFeatures, + messageSigningService: plugins.fleet?.messageSigningService, }); this.telemetryReceiver.start( diff --git a/x-pack/plugins/security_solution/tsconfig.json b/x-pack/plugins/security_solution/tsconfig.json index ac6c2b0c6e16c..373c561888a23 100644 --- a/x-pack/plugins/security_solution/tsconfig.json +++ b/x-pack/plugins/security_solution/tsconfig.json @@ -17,6 +17,7 @@ "exclude": [ "target/**/*", "**/cypress/**", + "public/management/cypress_endpoint.config.ts", ], "kbn_references": [ "@kbn/core", @@ -140,5 +141,7 @@ "@kbn/securitysolution-ecs", "@kbn/cell-actions", "@kbn/shared-ux-router", + "@kbn/alerts-as-data-utils", + "@kbn/expandable-flyout", ] } diff --git a/x-pack/plugins/stack_alerts/server/rule_types/es_query/lib/fetch_search_source_query.test.ts b/x-pack/plugins/stack_alerts/server/rule_types/es_query/lib/fetch_search_source_query.test.ts index 9a4026b582381..9afffb7c10ceb 100644 --- a/x-pack/plugins/stack_alerts/server/rule_types/es_query/lib/fetch_search_source_query.test.ts +++ b/x-pack/plugins/stack_alerts/server/rule_types/es_query/lib/fetch_search_source_query.test.ts @@ -124,6 +124,7 @@ describe('fetchSearchSourceQuery', () => { Object { "range": Object { "time": Object { + "format": "strict_date_optional_time", "gt": "2020-02-09T23:12:41.941Z", }, }, diff --git a/x-pack/plugins/stack_alerts/server/rule_types/es_query/lib/fetch_search_source_query.ts b/x-pack/plugins/stack_alerts/server/rule_types/es_query/lib/fetch_search_source_query.ts index e033f9c6ef4a8..bc22a228ce988 100644 --- a/x-pack/plugins/stack_alerts/server/rule_types/es_query/lib/fetch_search_source_query.ts +++ b/x-pack/plugins/stack_alerts/server/rule_types/es_query/lib/fetch_search_source_query.ts @@ -122,7 +122,11 @@ export function updateSearchSource( // add additional filter for documents with a timestamp greater then // the timestamp of the previous run, so that those documents are not counted twice const field = index.fields.find((f) => f.name === timeFieldName); - const addTimeRangeField = buildRangeFilter(field!, { gt: latestTimestamp }, index); + const addTimeRangeField = buildRangeFilter( + field!, + { gt: latestTimestamp, format: 'strict_date_optional_time' }, + index + ); filters.push(addTimeRangeField); } } diff --git a/x-pack/plugins/stack_connectors/public/connector_types/es_index/es_index_params.tsx b/x-pack/plugins/stack_connectors/public/connector_types/es_index/es_index_params.tsx index be34c715fb8d4..fc51ae0ba1ccf 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/es_index/es_index_params.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/es_index/es_index_params.tsx @@ -6,6 +6,7 @@ */ import React, { useEffect, useState } from 'react'; +import { isEmpty } from 'lodash'; import { EuiIcon, EuiText, @@ -45,10 +46,25 @@ export const IndexParamsFields = ({ ); const [isActionConnectorChanged, setIsActionConnectorChanged] = useState(false); - const getDocumentToIndex = (doc: Array> | undefined) => - doc && doc.length > 0 ? (doc[0] as unknown as string) : undefined; + const getDocumentToIndex = (docs: Array> | undefined) => { + // 'documents' param is stored as an array of objects but the JSON editor expects a single + // stringified object - const [documentToIndex, setDocumentToIndex] = useState( + // check that param is a non-empty array + return docs && docs.length > 0 + ? // if the array entry is a string, we can pass it directly to the JSON editor + typeof docs[0] === 'string' + ? docs[0] + : // otherwise check that the array entry is non-empty as sometimes we + // use an empty object to trigger validation but we don't want to auto-populate with an empty object + !isEmpty(docs[0]) + ? // if non-empty object, stringify it into format that JSON editor expects + JSON.stringify(docs[0], null, 2) + : null + : undefined; + }; + + const [documentToIndex, setDocumentToIndex] = useState( getDocumentToIndex(documents) ); const [alertHistoryIndexSuffix, setAlertHistoryIndexSuffix] = useState( @@ -58,9 +74,6 @@ export const IndexParamsFields = ({ useEffect(() => { setDocumentToIndex(getDocumentToIndex(documents)); - if (documents === null) { - setDocumentToIndex('{}'); - } }, [documents]); useEffect(() => { @@ -77,10 +90,14 @@ export const IndexParamsFields = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [actionConnector?.id]); - const onDocumentsChange = (updatedDocuments: string) => { + const onDocumentsChange = (updatedDocuments: string | null) => { try { - const documentsJSON = JSON.parse(updatedDocuments); - editAction('documents', [documentsJSON], index); + if (updatedDocuments != null) { + const documentsJSON = JSON.parse(updatedDocuments); + editAction('documents', [documentsJSON], index); + } else { + editAction('documents', updatedDocuments, index); + } setDocumentToIndex(updatedDocuments); } catch (e) { // set document as empty to turn on the validation for non empty valid JSON object @@ -180,11 +197,7 @@ export const IndexParamsFields = ({ messageVariables={messageVariables} paramsProperty={'documents'} data-test-subj="documentToIndex" - inputTargetValue={ - documentToIndex === null - ? '{}' // need this to trigger validation - : documentToIndex - } + inputTargetValue={documentToIndex} label={documentsFieldLabel} aria-label={i18n.translate('xpack.stackConnectors.components.index.jsonDocAriaLabel', { defaultMessage: 'Code editor', @@ -202,7 +215,7 @@ export const IndexParamsFields = ({ onBlur={() => { if (!documentToIndex) { // set document as empty to turn on the validation for non empty valid JSON object - onDocumentsChange('{}'); + onDocumentsChange(null); } }} /> diff --git a/x-pack/plugins/synthetics/common/rules/alert_actions.test.ts b/x-pack/plugins/synthetics/common/rules/alert_actions.test.ts index 19fc96ef86cf1..219790bfe991c 100644 --- a/x-pack/plugins/synthetics/common/rules/alert_actions.test.ts +++ b/x-pack/plugins/synthetics/common/rules/alert_actions.test.ts @@ -8,9 +8,11 @@ import { populateAlertActions } from './alert_actions'; import { ActionConnector } from './types'; import { MONITOR_STATUS } from '../constants/uptime_alerts'; +import { MONITOR_STATUS as SYNTHETICS_MONITOR_STATUS } from '../constants/synthetics_alerts'; import { MonitorStatusTranslations } from '../translations'; +import { SyntheticsMonitorStatusTranslations } from './synthetics/translations'; -describe('Alert Actions factory', () => { +describe('Legacy Alert Actions factory', () => { it('generate expected action for pager duty', async () => { const resp = populateAlertActions({ groupId: MONITOR_STATUS.id, @@ -32,6 +34,7 @@ describe('Alert Actions factory', () => { defaultRecoveryMessage: MonitorStatusTranslations.defaultRecoveryMessage, defaultSubjectMessage: MonitorStatusTranslations.defaultSubjectMessage, }, + isLegacy: true, }); expect(resp).toEqual([ { @@ -57,6 +60,66 @@ describe('Alert Actions factory', () => { ]); }); + it('generate expected action for index', async () => { + const resp = populateAlertActions({ + groupId: MONITOR_STATUS.id, + defaultActions: [ + { + actionTypeId: '.index', + group: 'xpack.uptime.alerts.actionGroups.monitorStatus', + params: { + dedupKey: 'always-downxpack.uptime.alerts.actionGroups.monitorStatus', + eventAction: 'trigger', + severity: 'error', + summary: MonitorStatusTranslations.defaultActionMessage, + }, + id: 'f2a3b195-ed76-499a-805d-82d24d4eeba9', + }, + ] as unknown as ActionConnector[], + translations: { + defaultActionMessage: MonitorStatusTranslations.defaultActionMessage, + defaultRecoveryMessage: MonitorStatusTranslations.defaultRecoveryMessage, + defaultSubjectMessage: MonitorStatusTranslations.defaultSubjectMessage, + }, + isLegacy: true, + }); + expect(resp).toEqual([ + { + group: 'recovered', + id: 'f2a3b195-ed76-499a-805d-82d24d4eeba9', + params: { + documents: [ + { + latestErrorMessage: '', + monitorName: '{{context.monitorName}}', + monitorUrl: '{{{context.monitorUrl}}}', + observerLocation: '{{context.observerLocation}}', + statusMessage: + 'Alert for monitor {{context.monitorName}} with url {{{context.monitorUrl}}} from {{context.observerLocation}} has recovered', + }, + ], + indexOverride: null, + }, + }, + { + group: 'xpack.uptime.alerts.actionGroups.monitorStatus', + id: 'f2a3b195-ed76-499a-805d-82d24d4eeba9', + params: { + documents: [ + { + latestErrorMessage: '{{{context.latestErrorMessage}}}', + monitorName: '{{context.monitorName}}', + monitorUrl: '{{{context.monitorUrl}}}', + observerLocation: '{{context.observerLocation}}', + statusMessage: '{{{context.statusMessage}}}', + }, + ], + indexOverride: null, + }, + }, + ]); + }); + it('generate expected action for slack action connector', async () => { const resp = populateAlertActions({ groupId: MONITOR_STATUS.id, @@ -104,3 +167,157 @@ describe('Alert Actions factory', () => { ]); }); }); + +describe('Alert Actions factory', () => { + it('generate expected action for pager duty', async () => { + const resp = populateAlertActions({ + groupId: SYNTHETICS_MONITOR_STATUS.id, + defaultActions: [ + { + actionTypeId: '.pagerduty', + group: 'xpack.uptime.alerts.actionGroups.monitorStatus', + params: { + dedupKey: 'always-downxpack.uptime.alerts.actionGroups.monitorStatus', + eventAction: 'trigger', + severity: 'error', + summary: SyntheticsMonitorStatusTranslations.defaultActionMessage, + }, + id: 'f2a3b195-ed76-499a-805d-82d24d4eeba9', + }, + ] as unknown as ActionConnector[], + translations: { + defaultActionMessage: SyntheticsMonitorStatusTranslations.defaultActionMessage, + defaultRecoveryMessage: SyntheticsMonitorStatusTranslations.defaultRecoveryMessage, + defaultSubjectMessage: SyntheticsMonitorStatusTranslations.defaultSubjectMessage, + }, + }); + expect(resp).toEqual([ + { + group: 'recovered', + id: 'f2a3b195-ed76-499a-805d-82d24d4eeba9', + params: { + dedupKey: expect.any(String), + eventAction: 'resolve', + summary: + 'The alert for the monitor {{context.monitorName}} checking {{{context.monitorUrl}}} from {{context.locationName}} is no longer active: {{context.recoveryReason}}.', + }, + }, + { + group: 'xpack.synthetics.alerts.actionGroups.monitorStatus', + id: 'f2a3b195-ed76-499a-805d-82d24d4eeba9', + params: { + dedupKey: expect.any(String), + eventAction: 'trigger', + severity: 'error', + summary: SyntheticsMonitorStatusTranslations.defaultActionMessage, + }, + }, + ]); + }); + + it('generate expected action for index', async () => { + const resp = populateAlertActions({ + groupId: SYNTHETICS_MONITOR_STATUS.id, + defaultActions: [ + { + actionTypeId: '.index', + group: 'xpack.synthetics.alerts.actionGroups.monitorStatus', + params: { + dedupKey: 'always-downxpack.uptime.alerts.actionGroups.monitorStatus', + eventAction: 'trigger', + severity: 'error', + summary: SyntheticsMonitorStatusTranslations.defaultActionMessage, + }, + id: 'f2a3b195-ed76-499a-805d-82d24d4eeba9', + }, + ] as unknown as ActionConnector[], + translations: { + defaultActionMessage: SyntheticsMonitorStatusTranslations.defaultActionMessage, + defaultRecoveryMessage: SyntheticsMonitorStatusTranslations.defaultRecoveryMessage, + defaultSubjectMessage: SyntheticsMonitorStatusTranslations.defaultSubjectMessage, + }, + }); + expect(resp).toEqual([ + { + group: 'recovered', + id: 'f2a3b195-ed76-499a-805d-82d24d4eeba9', + params: { + documents: [ + { + latestErrorMessage: '{{{context.latestErrorMessage}}}', + monitorName: '{{context.monitorName}}', + monitorUrl: '{{{context.monitorUrl}}}', + observerLocation: '{{context.locationName}}', + statusMessage: '{{{context.status}}}', + recoveryReason: '{{context.recoveryReason}}', + }, + ], + indexOverride: null, + }, + }, + { + group: 'xpack.synthetics.alerts.actionGroups.monitorStatus', + id: 'f2a3b195-ed76-499a-805d-82d24d4eeba9', + params: { + documents: [ + { + latestErrorMessage: '{{{context.lastErrorMessage}}}', + monitorName: '{{context.monitorName}}', + monitorUrl: '{{{context.monitorUrl}}}', + observerLocation: '{{context.locationName}}', + statusMessage: '{{{context.status}}}', + }, + ], + indexOverride: null, + }, + }, + ]); + }); + + it('generate expected action for slack action connector', async () => { + const resp = populateAlertActions({ + groupId: SYNTHETICS_MONITOR_STATUS.id, + defaultActions: [ + { + actionTypeId: '.pagerduty', + group: 'xpack.synthetics.alerts.actionGroups.monitorStatus', + params: { + dedupKey: 'always-downxpack.uptime.alerts.actionGroups.monitorStatus', + eventAction: 'trigger', + severity: 'error', + summary: + 'Monitor {{context.monitorName}} with url {{{context.monitorUrl}}} from {{context.observerLocation}} {{{context.statusMessage}}} The latest error message is {{{context.latestErrorMessage}}}', + }, + id: 'f2a3b195-ed76-499a-805d-82d24d4eeba9', + }, + ] as unknown as ActionConnector[], + translations: { + defaultActionMessage: SyntheticsMonitorStatusTranslations.defaultActionMessage, + defaultRecoveryMessage: SyntheticsMonitorStatusTranslations.defaultRecoveryMessage, + defaultSubjectMessage: SyntheticsMonitorStatusTranslations.defaultSubjectMessage, + }, + }); + expect(resp).toEqual([ + { + group: 'recovered', + id: 'f2a3b195-ed76-499a-805d-82d24d4eeba9', + params: { + dedupKey: expect.any(String), + eventAction: 'resolve', + summary: + 'The alert for the monitor {{context.monitorName}} checking {{{context.monitorUrl}}} from {{context.locationName}} is no longer active: {{context.recoveryReason}}.', + }, + }, + { + group: 'xpack.synthetics.alerts.actionGroups.monitorStatus', + id: 'f2a3b195-ed76-499a-805d-82d24d4eeba9', + params: { + dedupKey: expect.any(String), + eventAction: 'trigger', + severity: 'error', + summary: SyntheticsMonitorStatusTranslations.defaultActionMessage, + }, + }, + ]); + }); +}); diff --git a/x-pack/plugins/synthetics/common/rules/alert_actions.ts b/x-pack/plugins/synthetics/common/rules/alert_actions.ts index 9c32fbdf8d3cf..3f8cedf715536 100644 --- a/x-pack/plugins/synthetics/common/rules/alert_actions.ts +++ b/x-pack/plugins/synthetics/common/rules/alert_actions.ts @@ -43,11 +43,13 @@ export function populateAlertActions({ defaultEmail, groupId, translations, + isLegacy = false, }: { groupId: string; defaultActions: ActionConnector[]; defaultEmail?: DefaultEmail; translations: Translations; + isLegacy?: boolean; }) { const actions: RuleAction[] = []; defaultActions.forEach((aId) => { @@ -78,8 +80,8 @@ export function populateAlertActions({ actions.push(recoveredAction); break; case INDEX_ACTION_ID: - action.params = getIndexActionParams(translations); - recoveredAction.params = getIndexActionParams(translations, true); + action.params = getIndexActionParams(translations, false, isLegacy); + recoveredAction.params = getIndexActionParams(translations, true, isLegacy); actions.push(recoveredAction); break; case SERVICE_NOW_ACTION_ID: @@ -119,8 +121,12 @@ export function populateAlertActions({ return actions; } -function getIndexActionParams(translations: Translations, recovery = false): IndexActionParams { - if (recovery) { +function getIndexActionParams( + translations: Translations, + recovery = false, + isLegacy = false +): IndexActionParams { + if (isLegacy && recovery) { return { documents: [ { @@ -134,14 +140,45 @@ function getIndexActionParams(translations: Translations, recovery = false): Ind indexOverride: null, }; } + + if (isLegacy) { + return { + documents: [ + { + monitorName: '{{context.monitorName}}', + monitorUrl: '{{{context.monitorUrl}}}', + statusMessage: '{{{context.statusMessage}}}', + latestErrorMessage: '{{{context.latestErrorMessage}}}', + observerLocation: '{{context.observerLocation}}', + }, + ], + indexOverride: null, + }; + } + + if (recovery) { + return { + documents: [ + { + monitorName: '{{context.monitorName}}', + monitorUrl: '{{{context.monitorUrl}}}', + statusMessage: '{{{context.status}}}', + latestErrorMessage: '{{{context.latestErrorMessage}}}', + observerLocation: '{{context.locationName}}', + recoveryReason: '{{context.recoveryReason}}', + }, + ], + indexOverride: null, + }; + } return { documents: [ { monitorName: '{{context.monitorName}}', monitorUrl: '{{{context.monitorUrl}}}', - statusMessage: '{{{context.statusMessage}}}', - latestErrorMessage: '{{{context.latestErrorMessage}}}', - observerLocation: '{{context.observerLocation}}', + statusMessage: '{{{context.status}}}', + latestErrorMessage: '{{{context.lastErrorMessage}}}', + observerLocation: '{{context.locationName}}', }, ], indexOverride: null, diff --git a/x-pack/plugins/synthetics/common/rules/uptime_rule_field_map.ts b/x-pack/plugins/synthetics/common/rules/uptime_rule_field_map.ts index ff69d3a5e6e7f..be097ed8d8268 100644 --- a/x-pack/plugins/synthetics/common/rules/uptime_rule_field_map.ts +++ b/x-pack/plugins/synthetics/common/rules/uptime_rule_field_map.ts @@ -9,48 +9,62 @@ export const uptimeRuleFieldMap = { // common fields 'monitor.id': { type: 'keyword', + required: false, }, 'url.full': { type: 'keyword', + required: false, }, 'observer.geo.name': { type: 'keyword', + required: false, }, // monitor status alert fields 'error.message': { type: 'text', + required: false, }, 'agent.name': { type: 'keyword', + required: false, }, 'monitor.name': { type: 'keyword', + required: false, }, 'monitor.type': { type: 'keyword', + required: false, }, // tls alert fields 'tls.server.x509.issuer.common_name': { type: 'keyword', + required: false, }, 'tls.server.x509.subject.common_name': { type: 'keyword', + required: false, }, 'tls.server.x509.not_after': { type: 'date', + required: false, }, 'tls.server.x509.not_before': { type: 'date', + required: false, }, 'tls.server.hash.sha256': { type: 'keyword', + required: false, }, // anomaly alert fields 'anomaly.start': { type: 'date', + required: false, }, 'anomaly.bucket_span.minutes': { type: 'keyword', + required: false, }, } as const; diff --git a/x-pack/plugins/synthetics/common/runtime_types/monitor_management/synthetics_overview_status.ts b/x-pack/plugins/synthetics/common/runtime_types/monitor_management/synthetics_overview_status.ts index 027527a118d08..ca9c85fb1a481 100644 --- a/x-pack/plugins/synthetics/common/runtime_types/monitor_management/synthetics_overview_status.ts +++ b/x-pack/plugins/synthetics/common/runtime_types/monitor_management/synthetics_overview_status.ts @@ -6,8 +6,9 @@ */ import * as t from 'io-ts'; +import { ObserverCodec } from '../ping/observer'; import { ErrorStateCodec } from '../ping/error_state'; -import { AgentType, MonitorType, ObserverType, PingErrorType, UrlType } from '..'; +import { AgentType, MonitorType, PingErrorType, UrlType } from '..'; export const OverviewPingCode = t.interface({ '@timestamp': t.string, @@ -16,7 +17,7 @@ export const OverviewPingCode = t.interface({ up: t.number, }), monitor: MonitorType, - observer: ObserverType, + observer: ObserverCodec, config_id: t.string, error: PingErrorType, agent: AgentType, diff --git a/x-pack/plugins/synthetics/common/runtime_types/ping/error_state.ts b/x-pack/plugins/synthetics/common/runtime_types/ping/error_state.ts index 1830ff11f70e7..86b0b07052eac 100644 --- a/x-pack/plugins/synthetics/common/runtime_types/ping/error_state.ts +++ b/x-pack/plugins/synthetics/common/runtime_types/ping/error_state.ts @@ -8,7 +8,7 @@ import * as t from 'io-ts'; export const ErrorStateCodec = t.type({ - duration_ms: t.number, + duration_ms: t.string, checks: t.number, ends: t.union([t.string, t.null]), started_at: t.string, diff --git a/x-pack/plugins/synthetics/common/runtime_types/ping/observer.ts b/x-pack/plugins/synthetics/common/runtime_types/ping/observer.ts new file mode 100644 index 0000000000000..46aca34753cbb --- /dev/null +++ b/x-pack/plugins/synthetics/common/runtime_types/ping/observer.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; + +export const ObserverCodec = t.partial({ + hostname: t.string, + ip: t.array(t.string), + mac: t.array(t.string), + geo: t.partial({ + name: t.string, + continent_name: t.string, + city_name: t.string, + country_iso_code: t.string, + location: t.union([ + t.string, + t.partial({ lat: t.number, lon: t.number }), + t.partial({ lat: t.string, lon: t.string }), + ]), + }), +}); diff --git a/x-pack/plugins/synthetics/common/runtime_types/ping/ping.ts b/x-pack/plugins/synthetics/common/runtime_types/ping/ping.ts index 971c3116e2bf2..a241161c3cf92 100644 --- a/x-pack/plugins/synthetics/common/runtime_types/ping/ping.ts +++ b/x-pack/plugins/synthetics/common/runtime_types/ping/ping.ts @@ -6,6 +6,7 @@ */ import * as t from 'io-ts'; +import { ObserverCodec } from './observer'; import { ErrorStateCodec } from './error_state'; import { DateRangeType } from '../common'; import { SyntheticsDataType } from './synthetics'; @@ -120,23 +121,6 @@ export const PingHeadersType = t.record(t.string, t.union([t.string, t.array(t.s export type PingHeaders = t.TypeOf; -export const ObserverType = t.partial({ - hostname: t.string, - ip: t.array(t.string), - mac: t.array(t.string), - geo: t.partial({ - name: t.string, - continent_name: t.string, - city_name: t.string, - country_iso_code: t.string, - location: t.union([ - t.string, - t.partial({ lat: t.number, lon: t.number }), - t.partial({ lat: t.string, lon: t.string }), - ]), - }), -}); - export const AgentType = t.intersection([ t.type({ ephemeral_id: t.string, @@ -213,7 +197,7 @@ export const PingType = t.intersection([ uid: t.string, }), }), - observer: ObserverType, + observer: ObserverCodec, resolve: t.partial({ ip: t.string, rtt: t.partial({ diff --git a/x-pack/plugins/synthetics/common/runtime_types/ping/synthetics.ts b/x-pack/plugins/synthetics/common/runtime_types/ping/synthetics.ts index 10fd7ecc6d2fe..793e1c05f5f0c 100644 --- a/x-pack/plugins/synthetics/common/runtime_types/ping/synthetics.ts +++ b/x-pack/plugins/synthetics/common/runtime_types/ping/synthetics.ts @@ -7,6 +7,7 @@ import { isRight } from 'fp-ts/lib/Either'; import * as t from 'io-ts'; +import { ObserverCodec } from './observer'; import { ErrorStateCodec } from './error_state'; /** @@ -75,11 +76,7 @@ export const JourneyStepType = t.intersection([ lt: t.string, }), }), - observer: t.partial({ - geo: t.type({ - name: t.string, - }), - }), + observer: ObserverCodec, synthetics: SyntheticsDataType, error: t.type({ message: t.string, diff --git a/x-pack/plugins/synthetics/e2e/journeys/synthetics/alerting_default.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/synthetics/alerting_default.journey.ts index 074cfc2c7dfcb..258be0cd93c59 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/synthetics/alerting_default.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/synthetics/alerting_default.journey.ts @@ -38,8 +38,6 @@ journey('AlertingDefaults', async ({ page, params }) => { }); step('Click text=Synthetics', async () => { - await page.click('text=Synthetics'); - await page.click('text=Settings'); expect(page.url()).toBe('http://localhost:5620/app/synthetics/settings/alerting'); await page.click('.euiComboBox__inputWrap'); await page.click("text=There aren't any options available"); diff --git a/x-pack/plugins/synthetics/e2e/journeys/synthetics/global_parameters.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/synthetics/global_parameters.journey.ts index a258f8d158d02..59b103d047250 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/synthetics/global_parameters.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/synthetics/global_parameters.journey.ts @@ -7,6 +7,7 @@ import { journey, step, before, after, expect } from '@elastic/synthetics'; import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { byTestId } from '@kbn/observability-plugin/e2e/utils'; import { cleanTestParams } from './services/add_monitor'; import { syntheticsAppPageProvider } from '../../page_objects/synthetics/synthetics_app'; @@ -32,7 +33,7 @@ journey(`GlobalParameters`, async ({ page, params }) => { }); step('Click text=Settings', async () => { - await page.click('text=Settings'); + await page.click(byTestId('settings-page-link')); expect(page.url()).toBe('http://localhost:5620/app/synthetics/settings/alerting'); }); step('Click text=Global Parameters', async () => { diff --git a/x-pack/plugins/synthetics/e2e/journeys/synthetics/monitor_selector.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/synthetics/monitor_selector.journey.ts index eec2c9fc7bf6f..758a3823332f2 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/synthetics/monitor_selector.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/synthetics/monitor_selector.journey.ts @@ -7,6 +7,7 @@ import { journey, step, expect, before, after } from '@elastic/synthetics'; import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { byTestId } from '@kbn/observability-plugin/e2e/utils'; import { addTestMonitor, cleanTestMonitors, @@ -50,7 +51,8 @@ journey(`MonitorSelector`, async ({ page, params }) => { }); step('shows recently viewed monitors', async () => { - await page.click('text=' + testMonitor1); + await page.waitForSelector(byTestId('monitorNameTitle')); + expect(await page.locator(byTestId('monitorNameTitle')).textContent()).toBe(testMonitor1); await page.click('[aria-label="Select a different monitor to view its details"]'); await page.click('text=' + testMonitor2); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_location_select.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_location_select.tsx index df50ebf152ec1..86ba0d3f50618 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_location_select.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_location_select.tsx @@ -115,6 +115,14 @@ export const MonitorLocationSelect = ({ ]); if (!selectedLocation || !monitorLocations) { + if (selectedLocation) { + return ( + + ); + } return ( { - return loading && !monitor ? ( - - ) : !status || status === 'unknown' ? ( + return !status || status === 'unknown' ? ( {PENDING_LABEL} @@ -49,6 +43,7 @@ export const MonitorStatus = ({ status?: string; }) => { const isBrowserType = monitor.type === 'browser'; + const loadingContent = loading && !monitor; return ( + description: loadingContent ? ( + + ) : ( + ), }, ]} diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/stderr_logs.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/stderr_logs.tsx index e58a105e80238..ac4d8caec8cb7 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/stderr_logs.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/stderr_logs.tsx @@ -113,9 +113,11 @@ export const StdErrorLogs = ({ - -

      {summaryMessage}

      -
      + {summaryMessage && ( + +

      {summaryMessage}

      +
      + )} )} diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/links/test_details_link.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/links/test_details_link.tsx index 2a34ff213bd0b..e4f5467954bd4 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/links/test_details_link.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/links/test_details_link.tsx @@ -10,8 +10,10 @@ import { EuiLink, EuiText, useEuiTheme } from '@elastic/eui'; import { useSelectedLocation } from '../../monitor_details/hooks/use_selected_location'; import { Ping } from '../../../../../../common/runtime_types'; import { useSyntheticsSettingsContext } from '../../../contexts'; -import { useKibanaDateFormat } from '../../../../../hooks/use_kibana_date_format'; -import { formatTestRunAt } from '../../../utils/monitor_test_result/test_time_formats'; +import { + formatTestRunAt, + useDateFormatForTest, +} from '../../../utils/monitor_test_result/test_time_formats'; export const TestDetailsLink = ({ isBrowserMonitor, @@ -26,7 +28,7 @@ export const TestDetailsLink = ({ const { basePath } = useSyntheticsSettingsContext(); const selectedLocation = useSelectedLocation(); - const format = useKibanaDateFormat(); + const format = useDateFormatForTest(); const timestampText = ( {formatTestRunAt(timestamp, format)} diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/components/failed_tests_list.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/components/failed_tests_list.tsx index c6d524e5f0742..33b9db6224666 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/components/failed_tests_list.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/components/failed_tests_list.tsx @@ -7,15 +7,14 @@ import { i18n } from '@kbn/i18n'; import React, { MouseEvent, useState } from 'react'; -import { EuiBasicTable, EuiLink, EuiSpacer, EuiText } from '@elastic/eui'; +import { EuiBasicTable, EuiSpacer, EuiText } from '@elastic/eui'; import { useHistory, useParams } from 'react-router-dom'; -import { useKibanaDateFormat } from '../../../../../hooks/use_kibana_date_format'; -import { Ping } from '../../../../../../common/runtime_types'; import { - formatTestDuration, - formatTestRunAt, -} from '../../../utils/monitor_test_result/test_time_formats'; -import { useSyntheticsSettingsContext } from '../../../contexts'; + getTestRunDetailRelativeLink, + TestDetailsLink, +} from '../../common/links/test_details_link'; +import { Ping } from '../../../../../../common/runtime_types'; +import { formatTestDuration } from '../../../utils/monitor_test_result/test_time_formats'; import { useSelectedLocation } from '../../monitor_details/hooks/use_selected_location'; export const FailedTestsList = ({ @@ -34,28 +33,21 @@ export const FailedTestsList = ({ const items = failedTests.slice(pageIndex * pageSize, pageIndex * pageSize + pageSize); - const { basePath } = useSyntheticsSettingsContext(); - const history = useHistory(); - const selectedLocation = useSelectedLocation(); - const format = useKibanaDateFormat(); - const columns = [ { field: '@timestamp', name: TIMESTAMP_LABEL, sortable: true, - render: (value: string, item: Ping) => { - return ( - - {formatTestRunAt(value, format)} - - ); - }, + render: (value: string, item: Ping) => ( + + ), }, { field: 'monitor.duration.us', @@ -79,7 +71,11 @@ export const FailedTestsList = ({ 'data-test-subj': `row-${state.id}`, onClick: (evt: MouseEvent) => { history.push( - `/monitor/${monitorId}/test-run/${item.monitor.check_group}?locationId=${selectedLocation?.id}` + getTestRunDetailRelativeLink({ + monitorId, + checkGroup: item.monitor.check_group, + locationId: selectedLocation?.id, + }) ); }, }; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/error_details_page.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/error_details_page.tsx index 1da468b8f882a..3174d85776733 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/error_details_page.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/error_details_page.tsx @@ -6,8 +6,9 @@ */ import React from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; +import { TestRunErrorInfo } from '../test_run_details/components/test_run_error_info'; import { StepDurationPanel } from '../monitor_details/monitor_summary/step_duration_panel'; import { useFormatTestRunAt } from '../../utils/monitor_test_result/test_time_formats'; import { LastTestRunComponent } from '../monitor_details/monitor_summary/last_test_run'; @@ -64,6 +65,10 @@ export function ErrorDetailsPage() { stepsLoading={stepsLoading} isErrorDetails={true} /> + + + + diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_journey_steps.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_journey_steps.tsx index 5901711986419..19f22f3cad165 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_journey_steps.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_journey_steps.tsx @@ -9,7 +9,7 @@ import { useParams } from 'react-router-dom'; import { useDispatch, useSelector } from 'react-redux'; import { useEffect } from 'react'; import { isStepEnd } from '../../common/monitor_test_result/browser_steps_list'; -import { JourneyStep, SyntheticsJourneyApiResponse } from '../../../../../../common/runtime_types'; +import { JourneyStep } from '../../../../../../common/runtime_types'; import { fetchJourneyAction, selectBrowserJourney, @@ -53,7 +53,7 @@ export const useJourneySteps = ( failedStep?.synthetics?.step && failedStep.synthetics.step.index === Number(stepIndex); return { - data: journeyData as SyntheticsJourneyApiResponse, + data: journeyData, loading: loading ?? false, stepEnds, stepLabels, diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_selected_monitor.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_selected_monitor.tsx index fe2d6028151db..e834c336d9694 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_selected_monitor.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_selected_monitor.tsx @@ -49,11 +49,15 @@ export const useSelectedMonitor = (monId?: string) => { ? monitorFromList : null; + const isMonitorMissing = + error?.body.statusCode === 404 && + (error.getPayload as { monitorId: string })?.monitorId === monitorId; + useEffect(() => { - if (monitorId && !availableMonitor && !syntheticsMonitorLoading) { + if (monitorId && !availableMonitor && !syntheticsMonitorLoading && !isMonitorMissing) { dispatch(getMonitorAction.get({ monitorId })); } - }, [dispatch, monitorId, availableMonitor, syntheticsMonitorLoading]); + }, [dispatch, monitorId, availableMonitor, syntheticsMonitorLoading, isMonitorMissing]); useEffect(() => { // Only perform periodic refresh if the last dispatch was earlier enough @@ -80,5 +84,6 @@ export const useSelectedMonitor = (monId?: string) => { monitor: availableMonitor, loading: syntheticsMonitorLoading || monitorListLoading, error, + isMonitorMissing, }; }; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_details_page_title.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_details_page_title.tsx index 7e083fabc314f..b03ca571fff2d 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_details_page_title.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_details_page_title.tsx @@ -15,7 +15,9 @@ export const MonitorDetailsPageTitle = () => { return ( - {monitor?.name} + + {monitor?.name} + diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_details_status.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_details_status.tsx index e0860a9186088..566fd55b1e451 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_details_status.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_details_status.tsx @@ -14,14 +14,19 @@ import { useMonitorLatestPing } from './hooks/use_monitor_latest_ping'; export const MonitorDetailsStatus = () => { const { latestPing, loading: pingsLoading } = useMonitorLatestPing(); - const { monitor } = useSelectedMonitor(); + const { monitor, isMonitorMissing } = useSelectedMonitor(); if (!monitor) { return ( }]} + listItems={[ + { + title: STATUS_LABEL, + description: isMonitorMissing ? <> : , + }, + ]} /> ); } diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/last_test_run.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/last_test_run.tsx index 48a8ec5962042..bb38b492d5055 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/last_test_run.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/last_test_run.tsx @@ -74,7 +74,7 @@ export const LastTestRunComponent = ({ stepsLoading: boolean; latestPing?: Ping; loading: boolean; - stepsData: SyntheticsJourneyApiResponse; + stepsData?: SyntheticsJourneyApiResponse; isErrorDetails?: boolean; }) => { const { monitor } = useSelectedMonitor(); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/monitor_alerts.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/monitor_alerts.tsx index 17f973a13d759..25a150eab71f5 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/monitor_alerts.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/monitor_alerts.tsx @@ -74,7 +74,9 @@ export const MonitorAlerts = ({ filters: [ { field: 'observer.geo.name', - values: [selectedLocation.label], + // in 8.6.0, observer.geo.name was mapped to the id, + // so we have to pass both values to maintain history + values: [selectedLocation.label, selectedLocation.id], }, ], }, @@ -117,7 +119,9 @@ export const MonitorAlerts = ({ { field: 'kibana.alert.status', values: ['active'] }, { field: 'observer.geo.name', - values: [selectedLocation.label], + // in 8.6.0, observer.geo.name was mapped to the id, + // so we have to pass both values to maintain history + values: [selectedLocation.label, selectedLocation.id], }, ], }, @@ -147,7 +151,9 @@ export const MonitorAlerts = ({ { field: 'kibana.alert.status', values: ['active'] }, { field: 'observer.geo.name', - values: [selectedLocation.label], + // in 8.6.0, observer.geo.name was mapped to the id, + // so we have to pass both values to maintain history + values: [selectedLocation.label, selectedLocation.id], }, ], color: theme.eui.euiColorVis7_behindText, @@ -176,7 +182,9 @@ export const MonitorAlerts = ({ { field: 'kibana.alert.status', values: ['recovered'] }, { field: 'observer.geo.name', - values: [selectedLocation.label], + // in 8.6.0, observer.geo.name was mapped to the id, + // so we have to pass both values to maintain history + values: [selectedLocation.label, selectedLocation.id], }, ], }, @@ -206,7 +214,9 @@ export const MonitorAlerts = ({ { field: 'kibana.alert.status', values: ['recovered'] }, { field: 'observer.geo.name', - values: [selectedLocation.label], + // in 8.6.0, observer.geo.name was mapped to the id, + // so we have to pass both values to maintain history + values: [selectedLocation.label, selectedLocation.id], }, ], color: theme.eui.euiColorVis0_behindText, diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/test_runs_table.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/test_runs_table.tsx index 22d124ffc4974..aa2effd95d19a 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/test_runs_table.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/test_runs_table.tsx @@ -9,29 +9,18 @@ import React, { MouseEvent, useMemo, useState } from 'react'; import { useHistory, useParams } from 'react-router-dom'; import { useSelector } from 'react-redux'; import { i18n } from '@kbn/i18n'; -import { - EuiBasicTable, - EuiBasicTableColumn, - EuiButtonEmpty, - EuiFlexGroup, - EuiFlexItem, - EuiLink, - EuiPanel, - EuiText, - EuiTitle, -} from '@elastic/eui'; +import { EuiBasicTable, EuiBasicTableColumn, EuiPanel, EuiText } from '@elastic/eui'; import { Criteria } from '@elastic/eui/src/components/basic_table/basic_table'; import { EuiTableSortingType } from '@elastic/eui/src/components/basic_table/table_types'; -import { MONITOR_HISTORY_ROUTE, MONITOR_TYPES } from '../../../../../../common/constants'; +import { TestRunsTableHeader } from './test_runs_table_header'; +import { MONITOR_TYPES } from '../../../../../../common/constants'; import { getTestRunDetailRelativeLink, TestDetailsLink, } from '../../common/links/test_details_link'; import { ConfigKey, DataStream, Ping } from '../../../../../../common/runtime_types'; import { formatTestDuration } from '../../../utils/monitor_test_result/test_time_formats'; -import { useGetUrlParams } from '../../../hooks'; -import { stringifyUrlParams } from '../../../utils/url_params'; import { sortPings } from '../../../utils/monitor_test_result/sort_pings'; import { selectPingsError } from '../../../state'; import { parseBadgeStatus, StatusBadge } from '../../common/monitor_test_result/status_badge'; @@ -58,7 +47,6 @@ export const TestRunsTable = ({ showViewHistoryButton = true, }: TestRunsTableProps) => { const history = useHistory(); - const params = useGetUrlParams(); const { monitorId } = useParams<{ monitorId: string }>(); const [page, setPage] = useState({ index: 0, size: 10 }); @@ -182,44 +170,11 @@ export const TestRunsTable = ({ return ( - - - -

      {paginable || pings?.length < 10 ? TEST_RUNS : LAST_10_TEST_RUNS}

      -
      -
      - - - {showViewHistoryButton ? ( - - - {i18n.translate('xpack.synthetics.monitorDetails.summary.viewHistory', { - defaultMessage: 'View History', - })} - - - ) : null} - -
      + { + const history = useHistory(); + const params = useGetUrlParams(); + + const { monitor } = useSelectedMonitor(); + + return ( + + + +

      {paginable || pings?.length < 10 ? TEST_RUNS : LAST_10_TEST_RUNS}

      +
      +
      + + + {showViewHistoryButton ? ( + + + {i18n.translate('xpack.synthetics.monitorDetails.summary.viewHistory', { + defaultMessage: 'View History', + })} + + + ) : null} + +
      + ); +}; + +const TEST_RUNS = i18n.translate('xpack.synthetics.monitorDetails.summary.testRuns', { + defaultMessage: 'Test Runs', +}); + +export const LAST_10_TEST_RUNS = i18n.translate( + 'xpack.synthetics.monitorDetails.summary.lastTenTestRuns', + { + defaultMessage: 'Last 10 Test Runs', + } +); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/use_monitor_details_page.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/use_monitor_details_page.tsx index 831bd9fb446c5..761322646188e 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/use_monitor_details_page.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/use_monitor_details_page.tsx @@ -14,17 +14,13 @@ import { useMonitorListBreadcrumbs } from '../monitors_page/hooks/use_breadcrumb import { useSelectedMonitor } from './hooks/use_selected_monitor'; export const useMonitorDetailsPage = () => { - const { monitor, error } = useSelectedMonitor(); + const { monitor, isMonitorMissing } = useSelectedMonitor(); const { monitorId } = useParams<{ monitorId: string }>(); useMonitorListBreadcrumbs(monitor ? [{ text: monitor?.name ?? '' }] : []); - if ( - error?.body.statusCode === 404 && - (error.getPayload as { monitorId: string })?.monitorId === monitorId && - monitor?.[ConfigKey.CONFIG_ID] !== monitorId - ) { + if (isMonitorMissing && monitor?.[ConfigKey.CONFIG_ID] !== monitorId) { return ; } return null; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/filter_group.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/filter_group.tsx index a7db1094ff262..4749e3c810116 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/filter_group.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/filter_group.tsx @@ -9,7 +9,6 @@ import React from 'react'; import { EuiFilterGroup } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { useSelector } from 'react-redux'; -import { ServiceLocations } from '../../../../../../../common/runtime_types'; import { selectServiceLocationsState } from '../../../../state'; import { @@ -20,10 +19,6 @@ import { import { useFilters } from './use_filters'; import { FilterButton } from './filter_button'; -export const findLocationItem = (query: string, locations: ServiceLocations) => { - return locations.find(({ id, label }) => query === id || label === query); -}; - export const FilterGroup = ({ handleFilterChange, }: { diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/list_filters.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/list_filters.tsx index 9e654da2078e3..4eec46469ca06 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/list_filters.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/list_filters.tsx @@ -5,14 +5,14 @@ * 2.0. */ -import React from 'react'; +import React, { memo } from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { FilterGroup } from './filter_group'; import { SearchField } from '../search_field'; import { SyntheticsMonitorFilterChangeHandler } from './filter_fields'; -export function ListFilters({ +export const ListFilters = memo(function ({ handleFilterChange, }: { handleFilterChange: SyntheticsMonitorFilterChangeHandler; @@ -27,4 +27,4 @@ export function ListFilters({
      ); -} +}); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/hooks/use_monitor_list.test.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/hooks/use_monitor_list.test.tsx index 45504ab27b4a6..05741f7aa0cac 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/hooks/use_monitor_list.test.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/hooks/use_monitor_list.test.tsx @@ -40,7 +40,6 @@ describe('useMonitorList', () => { absoluteTotal: state.monitorList.data.absoluteTotal ?? 0, pageState: state.monitorList.pageState, syntheticsMonitors: selectEncryptedSyntheticsSavedMonitors.resultFunc(state.monitorList), - overviewStatus: null, handleFilterChange: jest.fn(), }; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/hooks/use_monitor_list.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/hooks/use_monitor_list.ts index 2e200e983d0a5..2235121b05ad5 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/hooks/use_monitor_list.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/hooks/use_monitor_list.ts @@ -8,7 +8,6 @@ import { useCallback, useEffect, useRef } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { useDebounce } from 'react-use'; -import { useOverviewStatus } from './use_overview_status'; import { fetchMonitorListAction, quietFetchMonitorListAction, @@ -27,8 +26,6 @@ export function useMonitorList() { const { pageState, loading, loaded, error, data } = useSelector(selectMonitorListState); const syntheticsMonitors = useSelector(selectEncryptedSyntheticsSavedMonitors); - const { status: overviewStatus } = useOverviewStatus(); - const { handleFilterChange } = useMonitorFiltersState(); const { lastRefresh } = useSyntheticsRefreshContext(); @@ -50,17 +47,20 @@ export function useMonitorList() { }, [lastRefresh]); // On initial mount, load the page - useEffect(() => { - if (isInitialMount.current) { - if (loaded) { - dispatch(quietFetchMonitorListAction(pageState)); - } else { - dispatch(fetchMonitorListAction.get(pageState)); + useDebounce( + () => { + if (isInitialMount.current) { + if (loaded) { + dispatch(quietFetchMonitorListAction(pageState)); + } else { + dispatch(fetchMonitorListAction.get(pageState)); + } } - } + }, + 100, // we don't use pageState here, for pageState, useDebounce will handle it - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [dispatch]); + [dispatch] + ); useDebounce( () => { @@ -86,7 +86,6 @@ export function useMonitorList() { loadPage, reloadPage, absoluteTotal: data.absoluteTotal ?? 0, - overviewStatus, handleFilterChange, }; } diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/hooks/use_overview_status.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/hooks/use_overview_status.ts index b7671ce9d55b2..ed99ec8161765 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/hooks/use_overview_status.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/hooks/use_overview_status.ts @@ -15,7 +15,7 @@ import { selectOverviewStatus, } from '../../../state/overview_status'; -export function useOverviewStatus() { +export function useOverviewStatus({ scopeStatusByLocation }: { scopeStatusByLocation: boolean }) { const pageState = useSelector(selectOverviewPageState); const { status, error, loaded } = useSelector(selectOverviewStatus); @@ -24,16 +24,16 @@ export function useOverviewStatus() { const dispatch = useDispatch(); const reload = useCallback(() => { - dispatch(fetchOverviewStatusAction.get(pageState)); - }, [dispatch, pageState]); + dispatch(fetchOverviewStatusAction.get({ pageState, scopeStatusByLocation })); + }, [dispatch, pageState, scopeStatusByLocation]); useEffect(() => { if (loaded) { - dispatch(quietFetchOverviewStatusAction.get(pageState)); + dispatch(quietFetchOverviewStatusAction.get({ pageState, scopeStatusByLocation })); } else { reload(); } - }, [dispatch, reload, lastRefresh, pageState, loaded]); + }, [dispatch, reload, lastRefresh, pageState, loaded, scopeStatusByLocation]); return { status, diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_container.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_container.tsx index 51b4932bdfe15..af5ddb09e7834 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_container.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_container.tsx @@ -8,11 +8,13 @@ import React from 'react'; import { EuiSpacer } from '@elastic/eui'; +import { useSelector } from 'react-redux'; import type { useMonitorList } from '../hooks/use_monitor_list'; import { MonitorAsyncError } from './monitor_errors/monitor_async_error'; import { ListFilters } from '../common/monitor_filters/list_filters'; import { MonitorList } from './monitor_list_table/monitor_list'; import { MonitorStats } from './monitor_stats/monitor_stats'; +import { selectOverviewStatus } from '../../../state/overview_status'; export const MonitorListContainer = ({ isEnabled, @@ -30,10 +32,11 @@ export const MonitorListContainer = ({ absoluteTotal, loadPage, reloadPage, - overviewStatus, handleFilterChange, } = monitorListProps; + const { status: overviewStatus } = useSelector(selectOverviewStatus); + // TODO: Display inline errors in the management table // const { errorSummaries, loading: errorsLoading } = useInlineErrors({ diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/monitors_page.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/monitors_page.tsx index 7f271efcdd876..ae9a03578ac22 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/monitors_page.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/monitors_page.tsx @@ -10,6 +10,7 @@ import { Redirect } from 'react-router-dom'; import { EuiButton, EuiCallOut, EuiLink, EuiSpacer } from '@elastic/eui'; import { useTrackPageview } from '@kbn/observability-plugin/public'; +import { useOverviewStatus } from './hooks/use_overview_status'; import { GETTING_STARTED_ROUTE } from '../../../../../common/constants'; import { ServiceAllowedWrapper } from '../common/wrappers/service_allowed_wrapper'; @@ -24,7 +25,7 @@ import { useMonitorListBreadcrumbs } from './hooks/use_breadcrumbs'; import { useMonitorList } from './hooks/use_monitor_list'; import * as labels from './management/labels'; -const MonitorPage: React.FC = () => { +const MonitorManagementPage: React.FC = () => { useTrackPageview({ app: 'synthetics', path: 'monitors' }); useTrackPageview({ app: 'synthetics', path: 'monitors', delay: 15000 }); @@ -37,6 +38,8 @@ const MonitorPage: React.FC = () => { enableSynthetics, } = useEnablement(); + useOverviewStatus({ scopeStatusByLocation: false }); + const monitorListProps = useMonitorList(); const { syntheticsMonitors, loading: monitorsLoading, absoluteTotal, loaded } = monitorListProps; @@ -91,6 +94,6 @@ const MonitorPage: React.FC = () => { export const MonitorsPageWithServiceAllowed = React.memo(() => ( - + )); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/actions_popover.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/actions_popover.tsx index ad86c58dce782..f3b50f5bf86cc 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/actions_popover.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/actions_popover.tsx @@ -76,6 +76,7 @@ interface Props { position: PopoverPosition; iconHasPanel?: boolean; iconSize?: 's' | 'xs'; + locationId?: string; } const CustomShadowPanel = styled(EuiPanel)<{ shadow: string }>` @@ -107,6 +108,7 @@ export function ActionsPopover({ position, iconHasPanel = true, iconSize = 's', + locationId, }: Props) { const euiShadow = useEuiShadow('l'); const dispatch = useDispatch(); @@ -117,7 +119,7 @@ export function ActionsPopover({ const detailUrl = useMonitorDetailLocator({ configId: monitor.configId, - locationId: monitor.location.id, + locationId: locationId ?? monitor.location.id, }); const editUrl = useEditMonitorLocator({ configId: monitor.configId }); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/grid_by_group/group_menu.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/grid_by_group/group_menu.tsx index 3247d40d05417..49b03ef09e609 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/grid_by_group/group_menu.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/grid_by_group/group_menu.tsx @@ -55,26 +55,26 @@ export const GroupMenu = ({ groupOptions, orderByOptions, groupField }: Props) = ); const items = [ - +

      {GROUP_TITLE}

      , ...groupOptions.map((option) => ( - + )), , - +

      {ORDER_BY_TITLE}

      , ...orderByOptions.map((option) => ( - + )), ]; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/monitor_detail_flyout.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/monitor_detail_flyout.tsx index f00e918f0b091..b34f4f6fe421a 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/monitor_detail_flyout.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/monitor_detail_flyout.tsx @@ -306,6 +306,7 @@ export function MonitorDetailFlyout(props: Props) { position="default" iconHasPanel={false} iconSize="xs" + locationId={locationId} /> )} diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_alerts.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_alerts.tsx index ffcec3f0aeb74..77a29f735a40b 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_alerts.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_alerts.tsx @@ -20,7 +20,7 @@ import { useKibana } from '@kbn/kibana-react-plugin/public'; import { useSelector } from 'react-redux'; import { selectOverviewStatus } from '../../../../state/overview_status'; import { AlertsLink } from '../../../common/links/view_alerts'; -import { useRefreshedRange } from '../../../../hooks'; +import { useRefreshedRange, useGetUrlParams } from '../../../../hooks'; import { ClientPluginsStart } from '../../../../../../plugin'; export const OverviewAlerts = () => { @@ -33,6 +33,8 @@ export const OverviewAlerts = () => { const { status } = useSelector(selectOverviewStatus); + const { locations } = useGetUrlParams(); + const loading = !status?.allIds || status?.allIds.length === 0; return ( @@ -66,6 +68,7 @@ export const OverviewAlerts = () => { status?.enabledMonitorQueryIds.length > 0 ? status?.enabledMonitorQueryIds : ['false-id'], + ...(locations?.length ? { 'observer.geo.name': locations } : {}), }, filters: [{ field: 'kibana.alert.status', values: ['active', 'recovered'] }], color: theme.eui.euiColorVis1, @@ -92,6 +95,7 @@ export const OverviewAlerts = () => { status?.enabledMonitorQueryIds.length > 0 ? status?.enabledMonitorQueryIds : ['false-id'], + ...(locations?.length ? { 'observer.geo.name': locations } : {}), }, dataType: 'alerts', selectedMetricField: RECORDS_FIELD, diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_errors/overview_errors.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_errors/overview_errors.tsx index 68548fa1af700..8f85b8bd90d43 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_errors/overview_errors.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_errors/overview_errors.tsx @@ -18,7 +18,7 @@ import { useSelector } from 'react-redux'; import { i18n } from '@kbn/i18n'; import { selectOverviewStatus } from '../../../../../state/overview_status'; import { OverviewErrorsSparklines } from './overview_errors_sparklines'; -import { useRefreshedRange } from '../../../../../hooks'; +import { useRefreshedRange, useGetUrlParams } from '../../../../../hooks'; import { OverviewErrorsCount } from './overview_errors_count'; export function OverviewErrors() { @@ -28,6 +28,8 @@ export function OverviewErrors() { const { from, to } = useRefreshedRange(6, 'hours'); + const params = useGetUrlParams(); + return ( @@ -43,6 +45,7 @@ export function OverviewErrors() { from={from} to={to} monitorIds={status?.enabledMonitorQueryIds ?? []} + locations={params.locations} /> @@ -50,6 +53,7 @@ export function OverviewErrors() { from={from} to={to} monitorIds={status?.enabledMonitorQueryIds ?? []} + locations={params.locations} /> diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_errors/overview_errors_count.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_errors/overview_errors_count.tsx index 1d0ce0a90fc3b..582e6f1988076 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_errors/overview_errors_count.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_errors/overview_errors_count.tsx @@ -16,13 +16,14 @@ interface MonitorErrorsCountProps { to: string; locationLabel?: string; monitorIds: string[]; + locations?: string[]; } export const OverviewErrorsCount = ({ monitorIds, from, to, - locationLabel, + locations, }: MonitorErrorsCountProps) => { const { observability } = useKibana().services; @@ -41,7 +42,7 @@ export const OverviewErrorsCount = ({ time, reportDefinitions: { 'monitor.id': monitorIds.length > 0 ? monitorIds : ['false-monitor-id'], - ...(locationLabel ? { 'observer.geo.name': [locationLabel] } : {}), + ...(locations?.length ? { 'observer.geo.name': locations } : {}), }, dataType: 'synthetics', selectedMetricField: 'monitor_errors', diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_errors/overview_errors_sparklines.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_errors/overview_errors_sparklines.tsx index 110aacad28a67..1a68495f63b3e 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_errors/overview_errors_sparklines.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_errors/overview_errors_sparklines.tsx @@ -15,8 +15,9 @@ interface Props { from: string; to: string; monitorIds: string[]; + locations?: string[]; } -export const OverviewErrorsSparklines = ({ from, to, monitorIds }: Props) => { +export const OverviewErrorsSparklines = ({ from, to, monitorIds, locations }: Props) => { const { observability } = useKibana().services; const { ExploratoryViewEmbeddable } = observability; @@ -38,6 +39,7 @@ export const OverviewErrorsSparklines = ({ from, to, monitorIds }: Props) => { seriesType: 'area', reportDefinitions: { 'monitor.id': monitorIds.length > 0 ? monitorIds : ['false-monitor-id'], + ...(locations?.length ? { 'observer.geo.name': locations } : {}), }, dataType: 'synthetics', selectedMetricField: 'monitor_errors', diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_status.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_status.tsx index 74f4539f3e91e..2842581994d83 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_status.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_status.tsx @@ -21,7 +21,7 @@ function title(t?: number) { export function OverviewStatus() { const { statusFilter } = useGetUrlParams(); - const { status, error: statusError } = useOverviewStatus(); + const { status, error: statusError } = useOverviewStatus({ scopeStatusByLocation: true }); const dispatch = useDispatch(); const [statusConfig, setStatusConfig] = useState({ up: status?.up, diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/sort_menu.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/sort_menu.tsx index e0aa448970972..f61966abd99f9 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/sort_menu.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/sort_menu.tsx @@ -54,24 +54,24 @@ export const SortMenu = ({ sortOptions, orderOptions, sortField }: Props) => { ); const items = [ - +

      {SORT_BY_TITLE}

      , ...sortOptions.map((option) => ( - + )), , - +

      {ORDER_BY_TITLE}

      , ...orderOptions.map((option) => ( - + )), ]; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/locations_table.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/locations_table.tsx index b6996d4e6149b..d5246581b2176 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/locations_table.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/locations_table.tsx @@ -129,6 +129,7 @@ export const PrivateLocationsTable = ({ const renderToolRight = () => { return [ { +export const StepPageNavigation = ({ testRunPage }: { testRunPage?: boolean }) => { const [isPopoverOpen, setIsPopoverOpen] = useState(false); const { data } = useJourneySteps(); + const { basePath } = useSyntheticsSettingsContext(); + const selectedLocation = useSelectedLocation(); let startedAt: string | ReactElement = useFormatTestRunAt(data?.details?.timestamp); - const { stepIndex } = useParams<{ stepIndex: string; monitorId: string }>(); + const { stepIndex, monitorId } = useParams<{ stepIndex: string; monitorId: string }>(); - const prevHref = useStepDetailLink({ + let prevHref = useStepDetailLink({ stepIndex, checkGroupId: data?.details?.previous?.checkGroup, }); - const nextHref = useStepDetailLink({ + let nextHref = useStepDetailLink({ stepIndex, checkGroupId: data?.details?.next?.checkGroup, }); + if (testRunPage && data?.details?.previous?.checkGroup && data?.details?.next?.checkGroup) { + prevHref = getTestRunDetailLink({ + basePath, + monitorId, + locationId: selectedLocation?.id, + checkGroup: data?.details?.previous?.checkGroup, + }); + nextHref = getTestRunDetailLink({ + basePath, + monitorId, + locationId: selectedLocation?.id, + checkGroup: data?.details?.next?.checkGroup, + }); + } + if (!startedAt) { startedAt = ; } @@ -63,7 +83,7 @@ export const StepPageNavigation = () => { button={ setIsPopoverOpen(true)} + onClick={() => setIsPopoverOpen((prev) => !prev)} iconType="arrowDown" iconSide="right" flush="left" diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/browser/browser_test_results.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/browser/browser_test_results.tsx index d2c497007b55f..4a0e08f2da975 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/browser/browser_test_results.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/browser/browser_test_results.tsx @@ -170,11 +170,11 @@ function getButtonContent({ ); } -const FAILED_TO_RUN = i18n.translate('xpack.synthetics.monitorManagement.failedRun', { +export const FAILED_TO_RUN = i18n.translate('xpack.synthetics.monitorManagement.failedRun', { defaultMessage: 'Failed to run steps', }); -const ERROR_RUNNING_TEST = i18n.translate('xpack.synthetics.testRun.runErrorLabel', { +export const ERROR_RUNNING_TEST = i18n.translate('xpack.synthetics.testRun.runErrorLabel', { defaultMessage: 'Error running test', }); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/components/step_details.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/components/step_details.tsx index 78952c85c1584..5ccd9ded9db3a 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/components/step_details.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/components/step_details.tsx @@ -23,11 +23,13 @@ export const StepDetails = ({ }: { loading: boolean; step?: JourneyStep; - stepsData: SyntheticsJourneyApiResponse; + stepsData?: SyntheticsJourneyApiResponse; stepIndex: number; totalSteps: number; setStepIndex: (stepIndex: number) => void; }) => { + if (totalSteps === 0 && !loading) return null; + return ( @@ -61,7 +63,7 @@ export const StepDetails = ({ - + ); }; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/components/test_run_date.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/components/test_run_date.tsx index 5be95611c7093..07281b34f8527 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/components/test_run_date.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/components/test_run_date.tsx @@ -5,22 +5,19 @@ * 2.0. */ -import React, { ReactElement } from 'react'; -import { EuiDescriptionList, EuiLoadingContent } from '@elastic/eui'; +import React from 'react'; +import { EuiDescriptionList } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { useJourneySteps } from '../../monitor_details/hooks/use_journey_steps'; -import { useFormatTestRunAt } from '../../../utils/monitor_test_result/test_time_formats'; +import { StepPageNavigation } from '../../step_details_page/step_page_nav'; export const TestRunDate = () => { - const { data } = useJourneySteps(); - - let startedAt: string | ReactElement = useFormatTestRunAt(data?.details?.timestamp); - - if (!startedAt) { - startedAt = ; - } - - return ; + return ( + }, + ]} + /> + ); }; const ERROR_DURATION = i18n.translate('xpack.synthetics.testDetails.date', { diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/components/test_run_details_status.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/components/test_run_details_status.tsx new file mode 100644 index 0000000000000..211864fbd4b2f --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/components/test_run_details_status.tsx @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; + +import { EuiDescriptionList, EuiLoadingContent } from '@elastic/eui'; +import { useJourneySteps } from '../../monitor_details/hooks/use_journey_steps'; +import { useSelectedMonitor } from '../../monitor_details/hooks/use_selected_monitor'; +import { BadgeStatus, MonitorStatus, STATUS_LABEL } from '../../common/components/monitor_status'; + +export const TestRunDetailsStatus = () => { + const { monitor, isMonitorMissing } = useSelectedMonitor(); + + const { data: stepsData, loading } = useJourneySteps(); + + if (!monitor) { + return ( + + ) : ( + + ), + }, + ]} + /> + ); + } + + return ( + + ); +}; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/components/test_run_error_info.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/components/test_run_error_info.tsx new file mode 100644 index 0000000000000..cbcd7316061c4 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/components/test_run_error_info.tsx @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiCallOut, EuiSpacer, EuiText } from '@elastic/eui'; +import * as React from 'react'; +import { SyntheticsJourneyApiResponse } from '../../../../../../common/runtime_types'; +import { StdErrorLogs } from '../../common/components/stderr_logs'; +import { + ERROR_RUNNING_TEST, + FAILED_TO_RUN, +} from '../../test_now_mode/browser/browser_test_results'; + +export const TestRunErrorInfo = ({ + journeyDetails, + hasNoSteps, + showErrorTitle = true, +}: { + hasNoSteps?: boolean; + showErrorTitle?: boolean; + journeyDetails: SyntheticsJourneyApiResponse['details']; +}) => { + const isDownMonitor = journeyDetails?.journey?.monitor?.status === 'down'; + + const errorMessage = journeyDetails?.journey?.error?.message; + + return ( + <> + {(hasNoSteps || isDownMonitor) && showErrorTitle && ( + + {errorMessage ?? FAILED_TO_RUN} + + )} + + {(hasNoSteps || isDownMonitor) && + errorMessage?.includes('journey did not finish executing') && ( + + )} + + ); +}; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/hooks/use_test_run_details_breadcrumbs.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/hooks/use_test_run_details_breadcrumbs.ts index ea2d3475a4781..d564ce89cf99d 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/hooks/use_test_run_details_breadcrumbs.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/hooks/use_test_run_details_breadcrumbs.ts @@ -27,13 +27,17 @@ export const useTestRunDetailsBreadcrumbs = ( text: MONITOR_MANAGEMENT_CRUMB, href: `${appPath}${MONITORS_ROUTE}`, }, - { - text: monitor?.name ?? '', - href: `${appPath}${MONITOR_ROUTE.replace( - ':monitorId', - monitor?.[ConfigKey.CONFIG_ID] ?? '' - )}?locationId=${selectedLocation?.id ?? ''}`, - }, + ...(monitor + ? [ + { + text: monitor?.name ?? '', + href: `${appPath}${MONITOR_ROUTE.replace( + ':monitorId', + monitor?.[ConfigKey.CONFIG_ID] ?? '' + )}?locationId=${selectedLocation?.id ?? ''}`, + }, + ] + : []), ...(extraCrumbs ?? []), ]); }; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/route_config.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/route_config.tsx index cb06e0f1392d1..b57c0fce680f1 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/route_config.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/route_config.tsx @@ -10,8 +10,8 @@ import { FormattedMessage } from '@kbn/i18n-react'; import React from 'react'; import { useHistory } from 'react-router-dom'; import { OutPortal } from 'react-reverse-portal'; +import { TestRunDetailsStatus } from './components/test_run_details_status'; import { RouteProps } from '../../routes'; -import { MonitorDetailsStatus } from '../monitor_details/monitor_details_status'; import { TestRunDate } from './components/test_run_date'; import { TEST_RUN_DETAILS_ROUTE } from '../../../../../common/constants'; import { TestRunDetails } from './test_run_details'; @@ -43,7 +43,7 @@ export const getTestRunDetailsRoute = ( defaultMessage="Test run details" /> ), - rightSideItems: [, , ], + rightSideItems: [, , ], }, }; }; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/step_tabs.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/step_tabs.tsx index 294d065bd7b00..26500d0874b5a 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/step_tabs.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/step_tabs.tsx @@ -54,6 +54,23 @@ export const StepTabs = ({ } }, [isFailedStep]); + const getBrowserConsoles = useCallback( + (index: number) => { + return stepsList + ?.filter( + (stepF) => + stepF.synthetics?.type === 'journey/browserconsole' && + stepF.synthetics?.step?.index! === index + ) + .map((stepF) => stepF.synthetics?.payload?.text!); + }, + [stepsList] + ); + + if (!loading && stepsList?.length === 0) { + return null; + } + const onSelectedTabChanged = (id: TabId) => { setSelectedTabId(id); }; @@ -103,19 +120,6 @@ export const StepTabs = ({ } }; - const getBrowserConsoles = useCallback( - (index: number) => { - return stepsList - ?.filter( - (stepF) => - stepF.synthetics?.type === 'journey/browserconsole' && - stepF.synthetics?.step?.index! === index - ) - .map((stepF) => stepF.synthetics?.payload?.text!); - }, - [stepsList] - ); - return ( <> {renderTabs()} diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/test_run_details.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/test_run_details.tsx index a72ccdeb3fa65..e05211ce69830 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/test_run_details.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/test_run_details.tsx @@ -10,6 +10,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiSpacer, EuiTitle } from '@elast import moment from 'moment'; import { FormattedMessage } from '@kbn/i18n-react'; import { useParams } from 'react-router-dom'; +import { TestRunErrorInfo } from './components/test_run_error_info'; import { MonitorDetailsPanelContainer } from '../monitor_details/monitor_summary/monitor_details_panel'; import { useSelectedLocation } from '../monitor_details/hooks/use_selected_location'; import { MonitorDetailsLinkPortal } from '../monitor_add_edit/monitor_details_portal'; @@ -40,53 +41,58 @@ export const TestRunDetails = () => { const stateId = stepsData?.details?.summary?.state?.id; + const hasNoSteps = stepsData?.steps.length === 0 && !stepsLoading; + return ( <> - - - - - - -

      - -

      -
      -
      - - { - setStepIndex(stepIndex + 1); - }} - handlePreviousStep={() => { - setStepIndex(stepIndex - 1); - }} - /> - -
      + + {!hasNoSteps && ( + + + + + + +

      + +

      +
      +
      + + { + setStepIndex(stepIndex + 1); + }} + handlePreviousStep={() => { + setStepIndex(stepIndex - 1); + }} + /> + +
      + + + + +
      - + +
      + + - -
      - - -
      - - - - - -
      + + + + )} {/* needed to render the monitor details link in breadcrumb*/} { }; }, [lastRefresh, refreshApp]); + useEvent( + 'visibilitychange', + () => { + const isOutdated = moment().diff(new Date(lastRefresh), 'seconds') > refreshInterval; + if (document.visibilityState !== 'hidden' && !refreshPaused && isOutdated) { + refreshApp(); + } + }, + document + ); + useEffect(() => { if (refreshPaused) { return; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_list/effects.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_list/effects.ts index 0d068f05f3488..def068c7db03a 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_list/effects.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_list/effects.ts @@ -100,11 +100,11 @@ export function* upsertMonitorEffect() { if (action.payload.shouldQuietFetchAfterSuccess !== false) { const monitorState = yield select(selectOverviewState); if (hasPageState(monitorState)) { + yield put(quietFetchOverviewAction.get(monitorState.pageState)); yield put( - quietFetchOverviewAction.get(monitorState.pageState as MonitorOverviewPageState) - ); - yield put( - quietFetchOverviewStatusAction.get(monitorState.pageState as MonitorOverviewPageState) + quietFetchOverviewStatusAction.get({ + pageState: monitorState.pageState, + }) ); } } diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/overview_status/actions.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/overview_status/actions.ts index 62ac7190e7a09..0ca6e0c308f41 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/overview_status/actions.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/overview_status/actions.ts @@ -12,12 +12,12 @@ import { createAsyncAction } from '../utils/actions'; import { OverviewStatus } from '../../../../../common/runtime_types'; export const fetchOverviewStatusAction = createAsyncAction< - MonitorOverviewPageState, + { pageState: MonitorOverviewPageState; scopeStatusByLocation?: boolean }, OverviewStatus >('fetchOverviewStatusAction'); export const quietFetchOverviewStatusAction = createAsyncAction< - MonitorOverviewPageState, + { pageState: MonitorOverviewPageState; scopeStatusByLocation?: boolean }, OverviewStatus >('quietFetchOverviewStatusAction'); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/overview_status/api.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/overview_status/api.ts index 517fecb08c51c..84fc548a64878 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/overview_status/api.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/overview_status/api.ts @@ -11,9 +11,17 @@ import { OverviewStatus, OverviewStatusCodec } from '../../../../../common/runti import { apiService } from '../../../../utils/api_service'; import { toStatusOverviewQueryArgs } from '../overview/api'; -export const fetchOverviewStatus = async ( - pageState: MonitorOverviewPageState -): Promise => { +export const fetchOverviewStatus = async ({ + pageState, + scopeStatusByLocation, +}: { + pageState: MonitorOverviewPageState; + scopeStatusByLocation?: boolean; +}): Promise => { const params = toStatusOverviewQueryArgs(pageState); - return apiService.get(SYNTHETICS_API_URLS.OVERVIEW_STATUS, params, OverviewStatusCodec); + return apiService.get( + SYNTHETICS_API_URLS.OVERVIEW_STATUS, + { ...params, scopeStatusByLocation }, + OverviewStatusCodec + ); }; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/utils/monitor_test_result/test_time_formats.test.ts b/x-pack/plugins/synthetics/public/apps/synthetics/utils/monitor_test_result/test_time_formats.test.ts new file mode 100644 index 0000000000000..5337468f6e730 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/utils/monitor_test_result/test_time_formats.test.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { formatTestDuration } from './test_time_formats'; + +describe('formatTestDuration', () => { + it.each` + duration | expected | isMilli + ${undefined} | ${'0 ms'} | ${undefined} + ${120_000_000} | ${'2 min'} | ${undefined} + ${6_200_000} | ${'6.2 s'} | ${false} + ${500_000} | ${'500 ms'} | ${undefined} + ${100} | ${'0 ms'} | ${undefined} + ${undefined} | ${'0 ms'} | ${true} + ${600_000} | ${'10 min'} | ${true} + ${6_200} | ${'6.2 s'} | ${true} + ${500} | ${'500 ms'} | ${true} + `( + 'returns $expected when `duration` is $duration and `isMilli` $isMilli', + ({ + duration, + expected, + isMilli, + }: { + duration?: number; + expected: string; + isMilli?: boolean; + }) => { + expect(formatTestDuration(duration, isMilli)).toBe(expected); + } + ); +}); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/utils/monitor_test_result/test_time_formats.ts b/x-pack/plugins/synthetics/public/apps/synthetics/utils/monitor_test_result/test_time_formats.ts index fcf07f1cf8714..5d605ad4c2192 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/utils/monitor_test_result/test_time_formats.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/utils/monitor_test_result/test_time_formats.ts @@ -17,11 +17,11 @@ export const formatTestDuration = (duration = 0, isMilli = false) => { const secs = isMilli ? duration / 1e3 : duration / 1e6; if (secs >= 60) { - return `${(secs / 60).toFixed(1)} min`; + return `${parseFloat((secs / 60).toFixed(1))} min`; } if (secs >= 1) { - return `${secs.toFixed(1)} s`; + return `${parseFloat(secs.toFixed(1))} s`; } if (isMilli) { diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_edit_extension_wrapper.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_edit_extension_wrapper.tsx index 8a2671c14cc6c..00d5e8bd7b940 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_edit_extension_wrapper.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_edit_extension_wrapper.tsx @@ -14,8 +14,8 @@ import type { } from '@kbn/fleet-plugin/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { useEditMonitorLocator } from '../../../apps/synthetics/hooks'; -import { PolicyConfig, MonitorFields } from './types'; -import { ConfigKey, DataStream, TLSFields } from './types'; +import type { PolicyConfig, MonitorFields, TLSFields } from './types'; +import { ConfigKey, DataStream } from './types'; import { SyntheticsPolicyEditExtension } from './synthetics_policy_edit_extension'; import { PolicyConfigContextProvider, @@ -80,10 +80,10 @@ export const SyntheticsPolicyEditExtensionWrapper = memo = { diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/edit_monitor_config.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/edit_monitor_config.tsx index 0c487eb1fdc3d..e80a305afa6fa 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/edit_monitor_config.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/edit_monitor_config.tsx @@ -52,8 +52,8 @@ export const EditMonitorConfig = ({ monitor, throttling }: Props) => { [ConfigKey.TLS_VERSION]: monitor[ConfigKey.TLS_VERSION], }; - enableTLS = Boolean(monitor[ConfigKey.METADATA].is_tls_enabled); - enableZipUrlTLS = Boolean(monitor[ConfigKey.METADATA].is_zip_url_tls_enabled); + enableTLS = Boolean(monitor[ConfigKey.METADATA]?.is_tls_enabled); + enableZipUrlTLS = Boolean(monitor[ConfigKey.METADATA]?.is_zip_url_tls_enabled); const formattedDefaultConfig: Partial = { [type]: monitor, diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/steps_list.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/steps_list.tsx index 67da833384eb9..42481e17e16d3 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/steps_list.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/steps_list.tsx @@ -16,7 +16,6 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { MouseEvent, useState } from 'react'; -import styled from 'styled-components'; import { JourneyStep } from '../../../../../common/runtime_types'; import { STATUS_LABEL } from '../../monitor/ping_list/translations'; import { COLLAPSE_LABEL, EXPAND_LABEL, STEP_NAME_LABEL } from '../translations'; @@ -28,10 +27,6 @@ import { useExpandedRow } from './use_expanded_row'; import { StepDuration } from './step_duration'; import { useUptimeSettingsContext } from '../../../contexts/uptime_settings_context'; -export const SpanWithMargin = styled.span` - margin-right: 16px; -`; - interface Props { data: JourneyStep[]; error?: Error; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/api/alerts.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/alerts.ts index e0f83c917aa8a..33cbac6468306 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/state/api/alerts.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/alerts.ts @@ -87,6 +87,7 @@ export const createAlert = async ({ defaultRecoveryMessage: MonitorStatusTranslations.defaultRecoveryMessage, defaultSubjectMessage: MonitorStatusTranslations.defaultSubjectMessage, }, + isLegacy: true, }); const data: NewMonitorStatusAlert = { diff --git a/x-pack/plugins/synthetics/server/alert_rules/common.test.ts b/x-pack/plugins/synthetics/server/alert_rules/common.test.ts index 34caffe5fcdd9..6280ebb3fdd5b 100644 --- a/x-pack/plugins/synthetics/server/alert_rules/common.test.ts +++ b/x-pack/plugins/synthetics/server/alert_rules/common.test.ts @@ -4,9 +4,11 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import { updateState } from './common'; +import { alertsMock } from '@kbn/alerting-plugin/server/mocks'; +import { IBasePath } from '@kbn/core/server'; +import { updateState, setRecoveredAlertsContext } from './common'; import { SyntheticsCommonState } from '../../common/runtime_types/alert_rules/common'; +import { StaleDownConfig } from './status_rule/status_rule_executor'; describe('updateState', () => { let spy: jest.SpyInstance; @@ -180,3 +182,153 @@ describe('updateState', () => { `); }); }); + +describe('setRecoveredAlertsContext', () => { + const { alertFactory } = alertsMock.createRuleExecutorServices(); + const { getRecoveredAlerts } = alertFactory.done(); + const alertUuid = 'alert-id'; + const location = 'US Central'; + const configId = '12345'; + const idWithLocation = `${configId}-${location}`; + const basePath = { + publicBaseUrl: 'https://localhost:5601', + } as IBasePath; + const getAlertUuid = () => alertUuid; + + const upConfigs = { + [idWithLocation]: { + configId, + monitorQueryId: 'stale-config', + status: 'up', + location: '', + ping: { + '@timestamp': new Date().toISOString(), + } as StaleDownConfig['ping'], + timestamp: new Date().toISOString(), + }, + }; + + it('sets context correctly when monitor is deleted', () => { + const setContext = jest.fn(); + getRecoveredAlerts.mockReturnValue([ + { + getId: () => alertUuid, + getState: () => ({ + idWithLocation, + monitorName: 'test-monitor', + }), + setContext, + }, + ]); + const staleDownConfigs = { + [idWithLocation]: { + configId, + monitorQueryId: 'stale-config', + status: 'down', + location: 'location', + ping: { + '@timestamp': new Date().toISOString(), + } as StaleDownConfig['ping'], + timestamp: new Date().toISOString(), + isDeleted: true, + }, + }; + setRecoveredAlertsContext({ + alertFactory, + basePath, + getAlertUuid, + spaceId: 'default', + staleDownConfigs, + upConfigs: {}, + }); + expect(setContext).toBeCalledWith({ + idWithLocation, + alertDetailsUrl: 'https://localhost:5601/app/observability/alerts/alert-id', + monitorName: 'test-monitor', + recoveryReason: 'Monitor has been deleted', + }); + }); + + it('sets context correctly when location is removed', () => { + const setContext = jest.fn(); + getRecoveredAlerts.mockReturnValue([ + { + getId: () => alertUuid, + getState: () => ({ + idWithLocation, + monitorName: 'test-monitor', + }), + setContext, + }, + ]); + const staleDownConfigs = { + [idWithLocation]: { + configId, + monitorQueryId: 'stale-config', + status: 'down', + location: 'location', + ping: { + '@timestamp': new Date().toISOString(), + } as StaleDownConfig['ping'], + timestamp: new Date().toISOString(), + isLocationRemoved: true, + }, + }; + setRecoveredAlertsContext({ + alertFactory, + basePath, + getAlertUuid, + spaceId: 'default', + staleDownConfigs, + upConfigs: {}, + }); + expect(setContext).toBeCalledWith({ + idWithLocation, + alertDetailsUrl: 'https://localhost:5601/app/observability/alerts/alert-id', + monitorName: 'test-monitor', + recoveryReason: 'Location has been removed from the monitor', + }); + }); + + it('sets context correctly when monitor is up', () => { + const setContext = jest.fn(); + getRecoveredAlerts.mockReturnValue([ + { + getId: () => alertUuid, + getState: () => ({ + idWithLocation, + monitorName: 'test-monitor', + }), + setContext, + }, + ]); + const staleDownConfigs = { + [idWithLocation]: { + configId, + monitorQueryId: 'stale-config', + status: 'down', + location: 'location', + ping: { + '@timestamp': new Date().toISOString(), + } as StaleDownConfig['ping'], + timestamp: new Date().toISOString(), + isLocationRemoved: true, + }, + }; + setRecoveredAlertsContext({ + alertFactory, + basePath, + getAlertUuid, + spaceId: 'default', + staleDownConfigs, + upConfigs, + }); + expect(setContext).toBeCalledWith({ + idWithLocation, + alertDetailsUrl: 'https://localhost:5601/app/observability/alerts/alert-id', + monitorName: 'test-monitor', + status: 'up', + recoveryReason: 'Monitor has recovered with status Up', + }); + }); +}); diff --git a/x-pack/plugins/synthetics/server/alert_rules/common.ts b/x-pack/plugins/synthetics/server/alert_rules/common.ts index 2b629e865a115..e12e85bf82ee2 100644 --- a/x-pack/plugins/synthetics/server/alert_rules/common.ts +++ b/x-pack/plugins/synthetics/server/alert_rules/common.ts @@ -80,12 +80,14 @@ export const setRecoveredAlertsContext = ({ getAlertUuid, spaceId, staleDownConfigs, + upConfigs, }: { alertFactory: RuleExecutorServices['alertFactory']; basePath?: IBasePath; getAlertUuid?: (alertId: string) => string | null; spaceId?: string; staleDownConfigs: AlertOverviewStatus['staleDownConfigs']; + upConfigs: AlertOverviewStatus['upConfigs']; }) => { const { getRecoveredAlerts } = alertFactory.done(); for (const alert of getRecoveredAlerts()) { @@ -95,6 +97,7 @@ export const setRecoveredAlertsContext = ({ const state = alert.getState() as SyntheticsCommonState; let recoveryReason = ''; + let isUp = false; if (state?.idWithLocation && staleDownConfigs[state.idWithLocation]) { const { idWithLocation } = state; @@ -110,8 +113,16 @@ export const setRecoveredAlertsContext = ({ } } + if (state?.idWithLocation && upConfigs[state.idWithLocation]) { + isUp = Boolean(upConfigs[state.idWithLocation]) || false; + recoveryReason = i18n.translate('xpack.synthetics.alerts.monitorStatus.upCheck', { + defaultMessage: `Monitor has recovered with status Up`, + }); + } + alert.setContext({ ...state, + ...(isUp ? { status: 'up' } : {}), ...(recoveryReason ? { [RECOVERY_REASON]: recoveryReason } : {}), ...(basePath && spaceId && alertUuid ? { [ALERT_DETAILS_URL]: getAlertDetailsUrl(basePath, spaceId, alertUuid) } diff --git a/x-pack/plugins/synthetics/server/alert_rules/status_rule/monitor_status_rule.ts b/x-pack/plugins/synthetics/server/alert_rules/status_rule/monitor_status_rule.ts index 7d309c1f595aa..e1719c1e84b93 100644 --- a/x-pack/plugins/synthetics/server/alert_rules/status_rule/monitor_status_rule.ts +++ b/x-pack/plugins/synthetics/server/alert_rules/status_rule/monitor_status_rule.ts @@ -84,7 +84,7 @@ export const registerSyntheticsStatusCheckRule = ( syntheticsMonitorClient ); - const { downConfigs, staleDownConfigs } = await statusRule.getDownChecks( + const { downConfigs, staleDownConfigs, upConfigs } = await statusRule.getDownChecks( ruleState.meta?.downConfigs as OverviewStatus['downConfigs'] ); @@ -129,6 +129,7 @@ export const registerSyntheticsStatusCheckRule = ( getAlertUuid, spaceId, staleDownConfigs, + upConfigs, }); return { diff --git a/x-pack/plugins/synthetics/server/alert_rules/status_rule/status_rule_executor.test.ts b/x-pack/plugins/synthetics/server/alert_rules/status_rule/status_rule_executor.test.ts new file mode 100644 index 0000000000000..8a79ae5beab13 --- /dev/null +++ b/x-pack/plugins/synthetics/server/alert_rules/status_rule/status_rule_executor.test.ts @@ -0,0 +1,197 @@ +/* + * Copyright 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 { StatusRuleExecutor } from './status_rule_executor'; +import { loggerMock } from '@kbn/logging-mocks'; +import { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks'; +import { UptimeServerSetup } from '../../legacy_uptime/lib/adapters'; +import { mockEncryptedSO } from '../../synthetics_service/utils/mocks'; +import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; +import { SyntheticsMonitorClient } from '../../synthetics_service/synthetics_monitor/synthetics_monitor_client'; +import { SyntheticsService } from '../../synthetics_service/synthetics_service'; +import moment from 'moment'; +import * as monitorUtils from '../../saved_objects/synthetics_monitor/get_all_monitors'; + +describe('StatusRuleExecutor', () => { + const mockEsClient = elasticsearchClientMock.createElasticsearchClient(); + const logger = loggerMock.create(); + const soClient = savedObjectsClientMock.create(); + + const serverMock: UptimeServerSetup = { + logger, + uptimeEsClient: mockEsClient, + authSavedObjectsClient: soClient, + config: { + service: { + username: 'dev', + password: '12345', + manifestUrl: 'http://localhost:8080/api/manifest', + }, + }, + spaces: { + spacesService: { + getSpaceId: jest.fn().mockReturnValue('test-space'), + }, + }, + encryptedSavedObjects: mockEncryptedSO, + } as unknown as UptimeServerSetup; + + const syntheticsService = new SyntheticsService(serverMock); + + const monitorClient = new SyntheticsMonitorClient(syntheticsService, serverMock); + + it('should only query enabled monitors', async () => { + const spy = jest.spyOn(monitorUtils, 'getAllMonitors').mockResolvedValue([]); + const statusRule = new StatusRuleExecutor( + moment().toDate(), + {}, + soClient, + mockEsClient, + serverMock, + monitorClient + ); + const { downConfigs, staleDownConfigs } = await statusRule.getDownChecks({}); + + expect(downConfigs).toEqual({}); + expect(staleDownConfigs).toEqual({}); + + expect(spy).toHaveBeenCalledWith({ + filter: 'synthetics-monitor.attributes.alert.status.enabled: true', + soClient, + }); + }); + it('marks deleted configs as expected', async () => { + jest.spyOn(monitorUtils, 'getAllMonitors').mockResolvedValue(testMonitors); + const statusRule = new StatusRuleExecutor( + moment().toDate(), + {}, + soClient, + mockEsClient, + serverMock, + monitorClient + ); + + const { downConfigs } = await statusRule.getDownChecks({}); + + expect(downConfigs).toEqual({}); + + const staleDownConfigs = await statusRule.markDeletedConfigs({ + id1: { + location: 'us-east-1', + configId: 'id1', + status: 'down', + timestamp: '2021-06-01T00:00:00.000Z', + monitorQueryId: 'test', + ping: {} as any, + }, + '2548dab3-4752-4b4d-89a2-ae3402b6fb04-us_central_dev': { + location: 'US Central DEV', + configId: '2548dab3-4752-4b4d-89a2-ae3402b6fb04', + status: 'down', + timestamp: '2021-06-01T00:00:00.000Z', + monitorQueryId: 'test', + ping: {} as any, + }, + '2548dab3-4752-4b4d-89a2-ae3402b6fb04-us_central_qa': { + location: 'US Central QA', + configId: '2548dab3-4752-4b4d-89a2-ae3402b6fb04', + status: 'down', + timestamp: '2021-06-01T00:00:00.000Z', + monitorQueryId: 'test', + ping: {} as any, + }, + }); + + expect(staleDownConfigs).toEqual({ + id1: { + configId: 'id1', + isDeleted: true, + location: 'us-east-1', + monitorQueryId: 'test', + ping: {}, + status: 'down', + timestamp: '2021-06-01T00:00:00.000Z', + }, + '2548dab3-4752-4b4d-89a2-ae3402b6fb04-us_central_dev': { + configId: '2548dab3-4752-4b4d-89a2-ae3402b6fb04', + isLocationRemoved: true, + location: 'US Central DEV', + monitorQueryId: 'test', + ping: {}, + status: 'down', + timestamp: '2021-06-01T00:00:00.000Z', + }, + }); + }); +}); + +const testMonitors = [ + { + type: 'synthetics-monitor', + id: '2548dab3-4752-4b4d-89a2-ae3402b6fb04', + attributes: { + type: 'browser', + form_monitor_type: 'multistep', + enabled: true, + alert: { status: { enabled: false } }, + schedule: { unit: 'm', number: '10' }, + 'service.name': '', + config_id: '2548dab3-4752-4b4d-89a2-ae3402b6fb04', + tags: [], + timeout: null, + name: 'https://www.google.com', + locations: [ + { + geo: { lon: -95.86, lat: 41.25 }, + isServiceManaged: true, + id: 'us_central_qa', + label: 'US Central QA', + }, + ], + namespace: 'test_monitor', + origin: 'ui', + journey_id: '', + hash: '', + id: '2548dab3-4752-4b4d-89a2-ae3402b6fb04', + project_id: '', + playwright_options: '', + __ui: { + script_source: { is_generated_script: false, file_name: '' }, + is_zip_url_tls_enabled: false, + }, + 'url.port': null, + 'source.zip_url.url': '', + 'source.zip_url.folder': '', + 'source.zip_url.proxy_url': '', + playwright_text_assertion: '', + urls: 'https://www.google.com', + screenshots: 'on', + 'filter_journeys.match': '', + 'filter_journeys.tags': [], + ignore_https_errors: false, + 'throttling.is_enabled': true, + 'throttling.download_speed': '5', + 'throttling.upload_speed': '3', + 'throttling.latency': '20', + 'throttling.config': '5d/3u/20l', + 'ssl.certificate_authorities': '', + 'ssl.certificate': '', + 'ssl.verification_mode': 'full', + 'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'], + revision: 3, + }, + references: [], + migrationVersion: { 'synthetics-monitor': '8.6.0' }, + coreMigrationVersion: '8.0.0', + updated_at: '2023-02-23T21:19:21.041Z', + created_at: '2023-02-23T21:04:19.579Z', + version: 'WzY1MjUwLDNd', + namespaces: ['test-monitor'], + score: null, + sort: ['https://www.google.com', 1889], + }, +] as any; diff --git a/x-pack/plugins/synthetics/server/alert_rules/status_rule/status_rule_executor.ts b/x-pack/plugins/synthetics/server/alert_rules/status_rule/status_rule_executor.ts index 1e28763c60a2b..923177dc6b377 100644 --- a/x-pack/plugins/synthetics/server/alert_rules/status_rule/status_rule_executor.ts +++ b/x-pack/plugins/synthetics/server/alert_rules/status_rule/status_rule_executor.ts @@ -10,13 +10,11 @@ import { SavedObjectsFindResult, } from '@kbn/core-saved-objects-api-server'; import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; -import { AlertConfigKey } from '../../../common/constants/monitor_management'; import { getAllLocations } from '../../synthetics_service/get_all_locations'; import { getAllMonitors, processMonitors, } from '../../saved_objects/synthetics_monitor/get_all_monitors'; -import { GetMonitorDownStatusMessageParams } from '../../legacy_uptime/lib/requests/get_monitor_status'; import { queryMonitorStatus } from '../../queries/query_monitor_status'; import { UptimeEsClient } from '../../legacy_uptime/lib/lib'; import { StatusRuleParams } from '../../../common/rules/status_rule'; @@ -26,9 +24,10 @@ import { OverviewStatus, OverviewStatusMetaData, } from '../../../common/runtime_types'; -import { statusCheckTranslations } from '../../legacy_uptime/lib/alerts/translations'; import { SyntheticsMonitorClient } from '../../synthetics_service/synthetics_monitor/synthetics_monitor_client'; import { UptimeServerSetup } from '../../legacy_uptime/lib/adapters'; +import { monitorAttributes } from '../../../common/types/saved_objects'; +import { AlertConfigKey } from '../../../common/constants/monitor_management'; export interface StaleDownConfig extends OverviewStatusMetaData { isDeleted?: boolean; @@ -67,11 +66,11 @@ export class StatusRuleExecutor { } async getAllLocationNames() { - const { publicLocations, privateLocations } = await getAllLocations( - this.server, - this.syntheticsMonitorClient, - this.soClient - ); + const { publicLocations, privateLocations } = await getAllLocations({ + server: this.server, + syntheticsMonitorClient: this.syntheticsMonitorClient, + savedObjectsClient: this.soClient, + }); publicLocations.forEach((loc) => { this.locationIdNameMap[loc.label] = loc.id; @@ -90,7 +89,7 @@ export class StatusRuleExecutor { await this.getAllLocationNames(); this.monitors = await getAllMonitors({ soClient: this.soClient, - search: `attributes.${AlertConfigKey.STATUS_ENABLED}: true`, + filter: `${monitorAttributes}.${AlertConfigKey.STATUS_ENABLED}: true`, }); const { @@ -204,16 +203,4 @@ export class StatusRuleExecutor { return staleDownConfigs; } - - async getStatusMessage(downMonParams?: GetMonitorDownStatusMessageParams) { - let statusMessage = ''; - if (downMonParams?.info) { - statusMessage = statusCheckTranslations.downMonitorsLabel( - downMonParams.count!, - downMonParams.interval!, - downMonParams.numTimes - ); - } - return statusMessage; - } } diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/synthetics_monitor.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/synthetics_monitor.ts index cb12f045a2311..0a766e4a5833d 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/synthetics_monitor.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/synthetics_monitor.ts @@ -133,6 +133,17 @@ export const getSyntheticsMonitorSavedObjectType = ( enabled: { type: 'boolean', }, + alert: { + properties: { + status: { + properties: { + enabled: { + type: 'boolean', + }, + }, + }, + }, + }, }, }, management: { diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/routes/types.ts b/x-pack/plugins/synthetics/server/legacy_uptime/routes/types.ts index ed555ed90abd0..55286f8ea770e 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/routes/types.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/routes/types.ts @@ -109,15 +109,7 @@ export type UMRouteHandler = ({ subject?: Subject; }) => IKibanaResponse | Promise>; -export type SyntheticsRouteHandler = ({ - uptimeEsClient, - context, - request, - response, - server, - savedObjectsClient, - subject: Subject, -}: { +export interface RouteContext { uptimeEsClient: UptimeEsClient; context: UptimeRequestHandlerContext; request: SyntheticsRequest; @@ -126,7 +118,17 @@ export type SyntheticsRouteHandler = ({ server: UptimeServerSetup; syntheticsMonitorClient: SyntheticsMonitorClient; subject?: Subject; -}) => IKibanaResponse | Promise>; +} + +export type SyntheticsRouteHandler = ({ + uptimeEsClient, + context, + request, + response, + server, + savedObjectsClient, + subject: Subject, +}: RouteContext) => IKibanaResponse | Promise>; export type SyntheticsStreamingRouteHandler = ({ uptimeEsClient, diff --git a/x-pack/plugins/synthetics/server/plugin.ts b/x-pack/plugins/synthetics/server/plugin.ts index 598fdd18b229b..c6120c70c3818 100644 --- a/x-pack/plugins/synthetics/server/plugin.ts +++ b/x-pack/plugins/synthetics/server/plugin.ts @@ -13,7 +13,7 @@ import { SavedObjectsClient, SavedObjectsClientContract, } from '@kbn/core/server'; -import { mappingFromFieldMap } from '@kbn/rule-registry-plugin/common/mapping_from_field_map'; +import { mappingFromFieldMap } from '@kbn/alerting-plugin/common'; import { experimentalRuleFieldMap } from '@kbn/rule-registry-plugin/common/assets/field_maps/experimental_rule_field_map'; import { Dataset } from '@kbn/rule-registry-plugin/server'; import { SyntheticsMonitorClient } from './synthetics_service/synthetics_monitor/synthetics_monitor_client'; diff --git a/x-pack/plugins/synthetics/server/queries/get_journey_details.ts b/x-pack/plugins/synthetics/server/queries/get_journey_details.ts index f5b67f8e0e6a4..55cd924403387 100644 --- a/x-pack/plugins/synthetics/server/queries/get_journey_details.ts +++ b/x-pack/plugins/synthetics/server/queries/get_journey_details.ts @@ -14,7 +14,7 @@ export interface GetJourneyDetails { checkGroup: string; } -type DocumentSource = (Ping & { '@timestamp': string }) | JourneyStep; +type DocumentSource = (Ping & { '@timestamp': string; synthetics: { type: string } }) | JourneyStep; export const getJourneyDetails: UMElasticsearchQueryFn< GetJourneyDetails, @@ -47,14 +47,19 @@ export const getJourneyDetails: UMElasticsearchQueryFn< 'getJourneyDetailsCurrentJourney' ); - const journeyStart = thisJourney.hits.hits.find( + const journeyStartHit = thisJourney.hits.hits.find( (hit) => hit._source.synthetics?.type === 'journey/start' ); - if (journeyStart) { - const { _id, _source } = journeyStart; - const thisJourneySource = Object.assign({ _id }, _source) as JourneyStep; + const journeySummaryHit = thisJourney.hits.hits.find( + ({ _source: summarySource }) => summarySource.synthetics?.type === 'heartbeat/summary' + ); + + const foundJourney = journeyStartHit || journeySummaryHit; + + const journeySource = journeyStartHit?._source ?? journeySummaryHit?._source; + if (journeySource && foundJourney) { const baseSiblingParams = createEsParams({ body: { query: { @@ -62,17 +67,17 @@ export const getJourneyDetails: UMElasticsearchQueryFn< filter: [ { term: { - 'monitor.id': thisJourneySource.monitor.id, + 'monitor.id': journeySource.monitor.id, }, }, { term: { - 'observer.geo.name': thisJourneySource.observer?.geo?.name, + 'observer.geo.name': journeySource.observer?.geo?.name, }, }, { - term: { - 'synthetics.type': 'journey/start', + terms: { + 'synthetics.type': ['journey/start', 'heartbeat/summary'], }, }, ] as QueryDslQueryContainer[], @@ -93,7 +98,7 @@ export const getJourneyDetails: UMElasticsearchQueryFn< { range: { '@timestamp': { - lt: thisJourneySource['@timestamp'], + lt: journeySource['@timestamp'], }, }, }, @@ -114,7 +119,7 @@ export const getJourneyDetails: UMElasticsearchQueryFn< { range: { '@timestamp': { - gt: thisJourneySource['@timestamp'], + gt: journeySource['@timestamp'], }, }, }, @@ -139,18 +144,30 @@ export const getJourneyDetails: UMElasticsearchQueryFn< const { body: previousJourneyResult } = previousJourneyPromise; const { body: nextJourneyResult } = nextJourneyPromise; - const previousJourney = - previousJourneyResult?.hits?.hits.length > 0 ? previousJourneyResult?.hits?.hits[0] : null; - const nextJourney = - nextJourneyResult?.hits?.hits.length > 0 ? nextJourneyResult?.hits?.hits[0] : null; + const previousJourney = previousJourneyResult?.hits?.hits?.[0]; + const nextJourney = nextJourneyResult?.hits?.hits?.[0]; const summaryPing = thisJourney.hits.hits.find( ({ _source: summarySource }) => summarySource.synthetics?.type === 'heartbeat/summary' )?._source; + const previousInfo = previousJourney + ? { + checkGroup: previousJourney._source.monitor.check_group, + timestamp: previousJourney._source['@timestamp'], + } + : undefined; + + const nextInfo = nextJourney + ? { + checkGroup: nextJourney._source.monitor.check_group, + timestamp: nextJourney._source['@timestamp'], + } + : undefined; + return { - timestamp: thisJourneySource['@timestamp'], - journey: thisJourneySource, + timestamp: journeySource['@timestamp'], + journey: { ...journeySource, _id: foundJourney._id }, ...(summaryPing && 'state' in summaryPing && summaryPing.state ? { summary: { @@ -158,18 +175,8 @@ export const getJourneyDetails: UMElasticsearchQueryFn< }, } : {}), - previous: previousJourney - ? { - checkGroup: previousJourney._source.monitor.check_group, - timestamp: previousJourney._source['@timestamp'], - } - : undefined, - next: nextJourney - ? { - checkGroup: nextJourney._source.monitor.check_group, - timestamp: nextJourney._source['@timestamp'], - } - : undefined, + previous: previousInfo, + next: nextInfo, }; } else { return null; diff --git a/x-pack/plugins/synthetics/server/routes/common.ts b/x-pack/plugins/synthetics/server/routes/common.ts index 2244e0296cf63..c536884e25c4e 100644 --- a/x-pack/plugins/synthetics/server/routes/common.ts +++ b/x-pack/plugins/synthetics/server/routes/common.ts @@ -6,11 +6,12 @@ */ import { schema, TypeOf } from '@kbn/config-schema'; -import { SavedObjectsClientContract, SavedObjectsFindResponse } from '@kbn/core/server'; -import { SyntheticsService } from '../synthetics_service/synthetics_service'; +import { SavedObjectsFindResponse } from '@kbn/core/server'; +import { getAllLocations } from '../synthetics_service/get_all_locations'; import { EncryptedSyntheticsMonitor, ServiceLocations } from '../../common/runtime_types'; import { monitorAttributes } from '../../common/types/saved_objects'; import { syntheticsMonitorType } from '../legacy_uptime/lib/saved_objects/synthetics_monitor'; +import { RouteContext } from '../legacy_uptime/routes'; export const QuerySchema = schema.object({ page: schema.maybe(schema.number()), @@ -40,6 +41,7 @@ export const OverviewStatusSchema = schema.object({ projects: schema.maybe(schema.oneOf([schema.string(), schema.arrayOf(schema.string())])), schedules: schema.maybe(schema.oneOf([schema.string(), schema.arrayOf(schema.string())])), status: schema.maybe(schema.oneOf([schema.string(), schema.arrayOf(schema.string())])), + scopeStatusByLocation: schema.maybe(schema.boolean()), }); export type OverviewStatusQuery = TypeOf; @@ -54,10 +56,8 @@ export const SEARCH_FIELDS = [ 'project_id.text', ]; -export const getMonitors = ( - request: MonitorsQuery, - syntheticsService: SyntheticsService, - savedObjectsClient: SavedObjectsClientContract +export const getMonitors = async ( + context: RouteContext ): Promise> => { const { perPage = 50, @@ -73,19 +73,19 @@ export const getMonitors = ( searchAfter, projects, schedules, - } = request as MonitorsQuery; + } = context.request.query as MonitorsQuery; - const filterStr = getMonitorFilters({ + const filterStr = await getMonitorFilters({ filter, monitorTypes, tags, locations, - serviceLocations: syntheticsService.locations, projects, schedules, + context, }); - return savedObjectsClient.find({ + return context.savedObjectsClient.find({ type: syntheticsMonitorType, perPage, page, @@ -99,15 +99,14 @@ export const getMonitors = ( }); }; -export const getMonitorFilters = ({ +export const getMonitorFilters = async ({ tags, - ports, filter, locations, projects, monitorTypes, schedules, - serviceLocations, + context, }: { filter?: string; tags?: string | string[]; @@ -115,10 +114,9 @@ export const getMonitorFilters = ({ locations?: string | string[]; projects?: string | string[]; schedules?: string | string[]; - ports?: string | string[]; - serviceLocations: ServiceLocations; + context: RouteContext; }) => { - const locationFilter = parseLocationFilter(serviceLocations, locations); + const locationFilter = await parseLocationFilter(context, locations); return [ filter, @@ -160,18 +158,20 @@ export const getKqlFilter = ({ return `${fieldKey}:"${values}"`; }; -const parseLocationFilter = (serviceLocations: ServiceLocations, locations?: string | string[]) => { - if (!locations) { +const parseLocationFilter = async (context: RouteContext, locations?: string | string[]) => { + if (!locations || locations?.length === 0) { return ''; } + const { allLocations } = await getAllLocations(context); + if (Array.isArray(locations)) { return locations - .map((loc) => findLocationItem(loc, serviceLocations)?.id ?? '') + .map((loc) => findLocationItem(loc, allLocations)?.id ?? '') .filter((val) => !!val); } - return findLocationItem(locations, serviceLocations)?.id ?? ''; + return findLocationItem(locations, allLocations)?.id ?? ''; }; export const findLocationItem = (query: string, locations: ServiceLocations) => { diff --git a/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor_project_legacy.ts b/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor_project_legacy.ts index 28fe5761a359f..b68d2ac7aa58c 100644 --- a/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor_project_legacy.ts +++ b/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor_project_legacy.ts @@ -45,11 +45,11 @@ export const addSyntheticsProjectMonitorRouteLegacy: SyntheticsStreamingRouteFac const { id: spaceId } = await server.spaces.spacesService.getActiveSpace(request); const { keep_stale: keepStale, project: projectId } = request.body || {}; - const { publicLocations, privateLocations } = await getAllLocations( + const { publicLocations, privateLocations } = await getAllLocations({ server, syntheticsMonitorClient, - savedObjectsClient - ); + savedObjectsClient, + }); const encryptedSavedObjectsClient = server.encryptedSavedObjects.getClient(); const pushMonitorFormatter = new ProjectMonitorFormatterLegacy({ diff --git a/x-pack/plugins/synthetics/server/routes/monitor_cruds/delete_monitor_project.ts b/x-pack/plugins/synthetics/server/routes/monitor_cruds/delete_monitor_project.ts index 9107180bfb1de..8a46dae9ff6a8 100644 --- a/x-pack/plugins/synthetics/server/routes/monitor_cruds/delete_monitor_project.ts +++ b/x-pack/plugins/synthetics/server/routes/monitor_cruds/delete_monitor_project.ts @@ -25,13 +25,8 @@ export const deleteSyntheticsMonitorProjectRoute: SyntheticsRestApiRouteFactory projectName: schema.string(), }), }, - handler: async ({ - request, - response, - savedObjectsClient, - server, - syntheticsMonitorClient, - }): Promise => { + handler: async (routeContext): Promise => { + const { request, response, savedObjectsClient, server, syntheticsMonitorClient } = routeContext; const { projectName } = request.params; const { monitors: monitorsToDelete } = request.body; const decodedProjectName = decodeURI(projectName); @@ -43,20 +38,20 @@ export const deleteSyntheticsMonitorProjectRoute: SyntheticsRestApiRouteFactory }); } - const { saved_objects: monitors } = await getMonitors( - { - filter: `${syntheticsMonitorType}.attributes.${ - ConfigKey.PROJECT_ID - }: "${decodedProjectName}" AND ${getKqlFilter({ - field: 'journey_id', - values: monitorsToDelete.map((id: string) => `${id}`), - })}`, - fields: [], - perPage: 500, + const deleteFilter = `${syntheticsMonitorType}.attributes.${ + ConfigKey.PROJECT_ID + }: "${decodedProjectName}" AND ${getKqlFilter({ + field: 'journey_id', + values: monitorsToDelete.map((id: string) => `${id}`), + })}`; + + const { saved_objects: monitors } = await getMonitors({ + ...routeContext, + request: { + ...request, + query: { ...request.query, filter: deleteFilter, fields: [], perPage: 500 }, }, - syntheticsMonitorClient.syntheticsService, - savedObjectsClient - ); + }); const { integrations: { writeIntegrationPolicies }, diff --git a/x-pack/plugins/synthetics/server/routes/monitor_cruds/get_monitor.ts b/x-pack/plugins/synthetics/server/routes/monitor_cruds/get_monitor.ts index 5adc119847e11..2557bf66db781 100644 --- a/x-pack/plugins/synthetics/server/routes/monitor_cruds/get_monitor.ts +++ b/x-pack/plugins/synthetics/server/routes/monitor_cruds/get_monitor.ts @@ -65,7 +65,8 @@ export const getAllSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () => validate: { query: QuerySchema, }, - handler: async ({ request, savedObjectsClient, syntheticsMonitorClient }): Promise => { + handler: async (routeContext): Promise => { + const { request, savedObjectsClient, syntheticsMonitorClient } = routeContext; const totalCountQuery = async () => { if (isMonitorsQueryFiltered(request.query)) { return savedObjectsClient.find({ @@ -77,7 +78,7 @@ export const getAllSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () => }; const [queryResult, totalCount] = await Promise.all([ - getMonitors(request.query, syntheticsMonitorClient.syntheticsService, savedObjectsClient), + getMonitors(routeContext), totalCountQuery(), ]); @@ -101,7 +102,9 @@ export const getSyntheticsMonitorOverviewRoute: SyntheticsRestApiRouteFactory = validate: { query: QuerySchema, }, - handler: async ({ request, savedObjectsClient, syntheticsMonitorClient }): Promise => { + handler: async (routeContext): Promise => { + const { request, savedObjectsClient } = routeContext; + const { sortField, sortOrder, @@ -109,9 +112,9 @@ export const getSyntheticsMonitorOverviewRoute: SyntheticsRestApiRouteFactory = locations: queriedLocations, } = request.query as MonitorsQuery; - const filtersStr = getMonitorFilters({ + const filtersStr = await getMonitorFilters({ ...request.query, - serviceLocations: syntheticsMonitorClient.syntheticsService.locations, + context: routeContext, }); const allMonitorConfigs = await getAllMonitors({ diff --git a/x-pack/plugins/synthetics/server/routes/monitor_cruds/get_monitor_project.ts b/x-pack/plugins/synthetics/server/routes/monitor_cruds/get_monitor_project.ts index fc965b94cb096..b9d5b07b4d481 100644 --- a/x-pack/plugins/synthetics/server/routes/monitor_cruds/get_monitor_project.ts +++ b/x-pack/plugins/synthetics/server/routes/monitor_cruds/get_monitor_project.ts @@ -25,31 +25,33 @@ export const getSyntheticsProjectMonitorsRoute: SyntheticsRestApiRouteFactory = }), query: querySchema, }, - handler: async ({ - request, - response, - server: { logger }, - savedObjectsClient, - syntheticsMonitorClient, - }): Promise => { + handler: async (routeContext): Promise => { + const { + request, + server: { logger }, + } = routeContext; + const { projectName } = request.params; const { per_page: perPage = 500, search_after: searchAfter } = request.query; const decodedProjectName = decodeURI(projectName); const decodedSearchAfter = searchAfter ? decodeURI(searchAfter) : undefined; try { - const { saved_objects: monitors, total } = await getMonitors( - { - filter: `${syntheticsMonitorType}.attributes.${ConfigKey.PROJECT_ID}: "${decodedProjectName}"`, - fields: [ConfigKey.JOURNEY_ID, ConfigKey.CONFIG_HASH], - perPage, - sortField: ConfigKey.JOURNEY_ID, - sortOrder: 'asc', - searchAfter: decodedSearchAfter ? [...decodedSearchAfter.split(',')] : undefined, + const { saved_objects: monitors, total } = await getMonitors({ + ...routeContext, + request: { + ...request, + query: { + ...request.query, + filter: `${syntheticsMonitorType}.attributes.${ConfigKey.PROJECT_ID}: "${decodedProjectName}"`, + fields: [ConfigKey.JOURNEY_ID, ConfigKey.CONFIG_HASH], + perPage, + sortField: ConfigKey.JOURNEY_ID, + sortOrder: 'asc', + searchAfter: decodedSearchAfter ? [...decodedSearchAfter.split(',')] : undefined, + }, }, - syntheticsMonitorClient.syntheticsService, - savedObjectsClient - ); + }); const projectMonitors = monitors.map((monitor) => ({ journey_id: monitor.attributes[ConfigKey.JOURNEY_ID], hash: monitor.attributes[ConfigKey.CONFIG_HASH] || '', diff --git a/x-pack/plugins/synthetics/server/routes/overview_status/overview_status.ts b/x-pack/plugins/synthetics/server/routes/overview_status/overview_status.ts index 670ebcab2d73c..750727d85f7ce 100644 --- a/x-pack/plugins/synthetics/server/routes/overview_status/overview_status.ts +++ b/x-pack/plugins/synthetics/server/routes/overview_status/overview_status.ts @@ -6,20 +6,16 @@ */ import { intersection } from 'lodash'; import datemath, { Unit } from '@kbn/datemath'; -import { SavedObjectsClientContract } from '@kbn/core/server'; import moment from 'moment'; import { ConfigKey } from '../../../common/runtime_types'; import { getAllMonitors, processMonitors, } from '../../saved_objects/synthetics_monitor/get_all_monitors'; -import { UptimeServerSetup } from '../../legacy_uptime/lib/adapters'; import { queryMonitorStatus } from '../../queries/query_monitor_status'; import { SYNTHETICS_API_URLS } from '../../../common/constants'; import { UMServerLibs } from '../../legacy_uptime/uptime_server'; -import { SyntheticsRestApiRouteFactory } from '../../legacy_uptime/routes'; -import { UptimeEsClient } from '../../legacy_uptime/lib/lib'; -import { SyntheticsMonitorClient } from '../../synthetics_service/synthetics_monitor/synthetics_monitor_client'; +import { RouteContext, SyntheticsRestApiRouteFactory } from '../../legacy_uptime/routes'; import { getMonitorFilters, OverviewStatusSchema, OverviewStatusQuery } from '../common'; /** @@ -40,14 +36,10 @@ export function periodToMs(schedule: { number: string; unit: Unit }) { * Subsequently, fetch the status for each monitor per location in the data streams. * @returns The counts of up/down/disabled monitor by location, and a map of each monitor:location status. */ -export async function getStatus( - server: UptimeServerSetup, - uptimeEsClient: UptimeEsClient, - soClient: SavedObjectsClientContract, - syntheticsMonitorClient: SyntheticsMonitorClient, - params: OverviewStatusQuery -) { - const { query, locations: queryLocations } = params; +export async function getStatus(context: RouteContext, params: OverviewStatusQuery) { + const { uptimeEsClient, syntheticsMonitorClient, savedObjectsClient, server } = context; + + const { query, locations: queryLocations, scopeStatusByLocation = true } = params; /** * Walk through all monitor saved objects, bucket IDs by disabled/enabled status. * @@ -55,12 +47,13 @@ export async function getStatus( * latest ping for all enabled monitors. */ - const filtersStr = getMonitorFilters({ + const filtersStr = await getMonitorFilters({ ...params, - serviceLocations: syntheticsMonitorClient.syntheticsService.locations, + context, }); + const allMonitors = await getAllMonitors({ - soClient, + soClient: savedObjectsClient, search: query ? `${query}*` : undefined, filter: filtersStr, fields: [ @@ -83,14 +76,15 @@ export async function getStatus( disabledMonitorsCount, projectMonitorsCount, monitorQueryIdToConfigIdMap, - } = await processMonitors(allMonitors, server, soClient, syntheticsMonitorClient); + } = await processMonitors(allMonitors, server, savedObjectsClient, syntheticsMonitorClient); // Account for locations filter const queryLocationsArray = queryLocations && !Array.isArray(queryLocations) ? [queryLocations] : queryLocations; - const listOfLocationAfterFilter = queryLocationsArray - ? intersection(listOfLocations, queryLocationsArray) - : listOfLocations; + const listOfLocationAfterFilter = + queryLocationsArray && scopeStatusByLocation + ? intersection(listOfLocations, queryLocationsArray) + : listOfLocations; const range = { from: moment().subtract(maxPeriod, 'milliseconds').subtract(20, 'minutes').toISOString(), @@ -128,20 +122,10 @@ export const createGetCurrentStatusRoute: SyntheticsRestApiRouteFactory = (libs: validate: { query: OverviewStatusSchema, }, - handler: async ({ - server, - uptimeEsClient, - savedObjectsClient, - syntheticsMonitorClient, - request, - }): Promise => { + handler: async (routeContext): Promise => { + const { request } = routeContext; + const params = request.query as OverviewStatusQuery; - return await getStatus( - server, - uptimeEsClient, - savedObjectsClient, - syntheticsMonitorClient, - params - ); + return await getStatus(routeContext, params); }, }); diff --git a/x-pack/plugins/synthetics/server/routes/synthetics_service/get_service_locations.ts b/x-pack/plugins/synthetics/server/routes/synthetics_service/get_service_locations.ts index 3f4f4fc72102c..7b4258ceb2a1a 100644 --- a/x-pack/plugins/synthetics/server/routes/synthetics_service/get_service_locations.ts +++ b/x-pack/plugins/synthetics/server/routes/synthetics_service/get_service_locations.ts @@ -14,14 +14,14 @@ export const getServiceLocationsRoute: SyntheticsRestApiRouteFactory = () => ({ path: API_URLS.SERVICE_LOCATIONS, validate: {}, handler: async ({ server, savedObjectsClient, syntheticsMonitorClient }): Promise => { - const { publicLocations, privateLocations, throttling } = await getAllLocations( + const { throttling, allLocations } = await getAllLocations({ server, syntheticsMonitorClient, - savedObjectsClient - ); + savedObjectsClient, + }); return { - locations: [...publicLocations, ...privateLocations], + locations: allLocations, throttling, }; }, diff --git a/x-pack/plugins/synthetics/server/saved_objects/synthetics_monitor/get_all_monitors.ts b/x-pack/plugins/synthetics/server/saved_objects/synthetics_monitor/get_all_monitors.ts index c8518823af95f..d134705093937 100644 --- a/x-pack/plugins/synthetics/server/saved_objects/synthetics_monitor/get_all_monitors.ts +++ b/x-pack/plugins/synthetics/server/saved_objects/synthetics_monitor/get_all_monitors.ts @@ -87,11 +87,11 @@ export const processMonitors = async ( const getLocationLabel = async (locationId: string) => { if (!allLocations) { - const { publicLocations, privateLocations } = await getAllLocations( + const { publicLocations, privateLocations } = await getAllLocations({ server, syntheticsMonitorClient, - soClient - ); + savedObjectsClient: soClient, + }); allLocations = [...publicLocations, ...privateLocations]; } diff --git a/x-pack/plugins/synthetics/server/synthetics_service/get_all_locations.ts b/x-pack/plugins/synthetics/server/synthetics_service/get_all_locations.ts index 41dbefa76a16e..d36e0928a62aa 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/get_all_locations.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/get_all_locations.ts @@ -10,20 +10,29 @@ import { getServiceLocations } from './get_service_locations'; import { SyntheticsMonitorClient } from './synthetics_monitor/synthetics_monitor_client'; import { UptimeServerSetup } from '../legacy_uptime/lib/adapters/framework'; -export async function getAllLocations( - server: UptimeServerSetup, - syntheticsMonitorClient: SyntheticsMonitorClient, - savedObjectsClient: SavedObjectsClientContract -) { +export async function getAllLocations({ + syntheticsMonitorClient, + savedObjectsClient, + server, +}: { + server: UptimeServerSetup; + syntheticsMonitorClient: SyntheticsMonitorClient; + savedObjectsClient: SavedObjectsClientContract; +}) { try { const [privateLocations, { locations: publicLocations, throttling }] = await Promise.all([ getPrivateLocations(syntheticsMonitorClient, savedObjectsClient), getServicePublicLocations(server, syntheticsMonitorClient), ]); - return { publicLocations, privateLocations, throttling }; + return { + publicLocations, + privateLocations, + throttling, + allLocations: [...publicLocations, ...privateLocations], + }; } catch (e) { server.logger.error(e); - return { publicLocations: [], privateLocations: [] }; + return { publicLocations: [], privateLocations: [], allLocations: [] }; } } diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter.ts index 796b14307048c..f888ddd9f34d8 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter.ts @@ -120,11 +120,11 @@ export class ProjectMonitorFormatter { } init = async () => { - const locationsPromise = getAllLocations( - this.server, - this.syntheticsMonitorClient, - this.savedObjectsClient - ); + const locationsPromise = getAllLocations({ + server: this.server, + syntheticsMonitorClient: this.syntheticsMonitorClient, + savedObjectsClient: this.savedObjectsClient, + }); const existingMonitorsPromise = this.getProjectMonitorsForProject(); const [locations, existingMonitors] = await Promise.all([ diff --git a/x-pack/plugins/synthetics/server/synthetics_service/synthetics_monitor/synthetics_monitor_client.ts b/x-pack/plugins/synthetics/server/synthetics_service/synthetics_monitor/synthetics_monitor_client.ts index 31be90c6d7827..d8d79ea1dfb05 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/synthetics_monitor/synthetics_monitor_client.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/synthetics_monitor/synthetics_monitor_client.ts @@ -30,7 +30,6 @@ import { syntheticsMonitorType } from '../../legacy_uptime/lib/saved_objects/syn export class SyntheticsMonitorClient { public syntheticsService: SyntheticsService; - public privateLocationAPI: SyntheticsPrivateLocation; constructor(syntheticsService: SyntheticsService, server: UptimeServerSetup) { diff --git a/x-pack/plugins/task_manager/server/constants.ts b/x-pack/plugins/task_manager/server/constants.ts index e843a0b4815d1..e9294b25e2859 100644 --- a/x-pack/plugins/task_manager/server/constants.ts +++ b/x-pack/plugins/task_manager/server/constants.ts @@ -5,3 +5,13 @@ * 2.0. */ export const TASK_MANAGER_INDEX = '.kibana_task_manager'; +export const CONCURRENCY_ALLOW_LIST_BY_TASK_TYPE: string[] = [ + // for testing + 'sampleTaskWithSingleConcurrency', + 'sampleTaskWithLimitedConcurrency', + 'timedTaskWithSingleConcurrency', + 'timedTaskWithLimitedConcurrency', + + // task types requiring a concurrency + 'report:execute', +]; diff --git a/x-pack/plugins/task_manager/server/ephemeral_task_lifecycle.test.ts b/x-pack/plugins/task_manager/server/ephemeral_task_lifecycle.test.ts index d6d0881cf0d50..374a52ea84b2f 100644 --- a/x-pack/plugins/task_manager/server/ephemeral_task_lifecycle.test.ts +++ b/x-pack/plugins/task_manager/server/ephemeral_task_lifecycle.test.ts @@ -22,6 +22,10 @@ import { TaskPoolMock } from './task_pool.mock'; import { executionContextServiceMock } from '@kbn/core/server/mocks'; import { taskManagerMock } from './mocks'; +jest.mock('./constants', () => ({ + CONCURRENCY_ALLOW_LIST_BY_TASK_TYPE: ['report'], +})); + const executionContext = executionContextServiceMock.createSetupContract(); describe('EphemeralTaskLifecycle', () => { diff --git a/x-pack/plugins/task_manager/server/polling_lifecycle.test.ts b/x-pack/plugins/task_manager/server/polling_lifecycle.test.ts index f4616b2f8c205..51cac494d3a67 100644 --- a/x-pack/plugins/task_manager/server/polling_lifecycle.test.ts +++ b/x-pack/plugins/task_manager/server/polling_lifecycle.test.ts @@ -31,6 +31,10 @@ jest.mock('./queries/task_claiming', () => { }; }); +jest.mock('./constants', () => ({ + CONCURRENCY_ALLOW_LIST_BY_TASK_TYPE: ['report', 'quickReport'], +})); + describe('TaskPollingLifecycle', () => { let clock: sinon.SinonFakeTimers; const taskManagerLogger = mockLogger(); diff --git a/x-pack/plugins/task_manager/server/queries/task_claiming.test.ts b/x-pack/plugins/task_manager/server/queries/task_claiming.test.ts index d847165bdba9d..6d24e4c0f5987 100644 --- a/x-pack/plugins/task_manager/server/queries/task_claiming.test.ts +++ b/x-pack/plugins/task_manager/server/queries/task_claiming.test.ts @@ -27,6 +27,17 @@ import { taskStoreMock } from '../task_store.mock'; import apm from 'elastic-apm-node'; import { TASK_MANAGER_TRANSACTION_TYPE } from '../task_running'; +jest.mock('../constants', () => ({ + CONCURRENCY_ALLOW_LIST_BY_TASK_TYPE: [ + 'limitedToZero', + 'limitedToOne', + 'anotherLimitedToZero', + 'anotherLimitedToOne', + 'limitedToTwo', + 'limitedToFive', + ], +})); + const taskManagerLogger = mockLogger(); beforeEach(() => jest.clearAllMocks()); diff --git a/x-pack/plugins/task_manager/server/task_scheduling.test.ts b/x-pack/plugins/task_manager/server/task_scheduling.test.ts index 4433a0ad83e8c..7f59f9f362554 100644 --- a/x-pack/plugins/task_manager/server/task_scheduling.test.ts +++ b/x-pack/plugins/task_manager/server/task_scheduling.test.ts @@ -27,6 +27,10 @@ jest.mock('uuid', () => ({ v4: () => 'v4uuid', })); +jest.mock('./constants', () => ({ + CONCURRENCY_ALLOW_LIST_BY_TASK_TYPE: ['foo'], +})); + jest.mock('elastic-apm-node', () => ({ currentTraceparent: 'parent', currentTransaction: { diff --git a/x-pack/plugins/task_manager/server/task_type_dictionary.test.ts b/x-pack/plugins/task_manager/server/task_type_dictionary.test.ts index cb2f436fa8676..039403816da5e 100644 --- a/x-pack/plugins/task_manager/server/task_type_dictionary.test.ts +++ b/x-pack/plugins/task_manager/server/task_type_dictionary.test.ts @@ -14,6 +14,10 @@ import { TaskTypeDictionary, } from './task_type_dictionary'; +jest.mock('./constants', () => ({ + CONCURRENCY_ALLOW_LIST_BY_TASK_TYPE: ['foo'], +})); + interface Opts { numTasks: number; } @@ -182,7 +186,6 @@ describe('taskTypeDictionary', () => { definitions.registerTaskDefinitions({ foo: { title: 'foo', - maxConcurrency: 2, createTaskRunner: jest.fn(), }, }); @@ -209,5 +212,19 @@ describe('taskTypeDictionary', () => { `"Task sampleTaskRemovedType has been removed from registration!"` ); }); + + it(`throws error when setting maxConcurrency to a task type that isn't allowed to set it`, () => { + expect(() => { + definitions.registerTaskDefinitions({ + foo2: { + title: 'foo2', + maxConcurrency: 2, + createTaskRunner: jest.fn(), + }, + }); + }).toThrowErrorMatchingInlineSnapshot( + `"maxConcurrency setting isn't allowed for task type: foo2"` + ); + }); }); }); diff --git a/x-pack/plugins/task_manager/server/task_type_dictionary.ts b/x-pack/plugins/task_manager/server/task_type_dictionary.ts index 9aeafa0a18c7d..3c0e4a0fe5542 100644 --- a/x-pack/plugins/task_manager/server/task_type_dictionary.ts +++ b/x-pack/plugins/task_manager/server/task_type_dictionary.ts @@ -7,6 +7,7 @@ import { Logger } from '@kbn/core/server'; import { TaskDefinition, taskDefinitionSchema, TaskRunCreatorFunction } from './task'; +import { CONCURRENCY_ALLOW_LIST_BY_TASK_TYPE } from './constants'; /** * Types that are no longer registered and will be marked as unregistered @@ -129,12 +130,25 @@ export class TaskTypeDictionary { throw new Error(`Task ${removed} has been removed from registration!`); } + for (const taskType of Object.keys(taskDefinitions)) { + if ( + taskDefinitions[taskType].maxConcurrency !== undefined && + !CONCURRENCY_ALLOW_LIST_BY_TASK_TYPE.includes(taskType) + ) { + // maxConcurrency is designed to limit how many tasks of the same type a single Kibana + // instance should run at a time. Meaning if you have 8 Kibanas running, you will still + // see up to 8 tasks running at a time but one per Kibana instance. This is helpful for + // reporting purposes but not for many other cases and are better off not setting this value. + throw new Error(`maxConcurrency setting isn't allowed for task type: ${taskType}`); + } + } + try { for (const definition of sanitizeTaskDefinitions(taskDefinitions)) { this.definitions.set(definition.type, definition); } } catch (e) { - this.logger.error('Could not sanitize task definitions'); + this.logger.error(`Could not sanitize task definitions: ${e.message}`); } } } diff --git a/x-pack/plugins/transform/public/app/common/pivot_aggs.ts b/x-pack/plugins/transform/public/app/common/pivot_aggs.ts index a1b8b6fefed0a..f22b5fcb264cc 100644 --- a/x-pack/plugins/transform/public/app/common/pivot_aggs.ts +++ b/x-pack/plugins/transform/public/app/common/pivot_aggs.ts @@ -32,7 +32,11 @@ export const TERMS_AGG_DEFAULT_SIZE = 10; export const pivotAggsFieldSupport = { [KBN_FIELD_TYPES.ATTACHMENT]: [PIVOT_SUPPORTED_AGGS.VALUE_COUNT, PIVOT_SUPPORTED_AGGS.FILTER], - [KBN_FIELD_TYPES.BOOLEAN]: [PIVOT_SUPPORTED_AGGS.VALUE_COUNT, PIVOT_SUPPORTED_AGGS.FILTER], + [KBN_FIELD_TYPES.BOOLEAN]: [ + PIVOT_SUPPORTED_AGGS.VALUE_COUNT, + PIVOT_SUPPORTED_AGGS.FILTER, + PIVOT_SUPPORTED_AGGS.TERMS, + ], [KBN_FIELD_TYPES.DATE]: [ PIVOT_SUPPORTED_AGGS.MAX, PIVOT_SUPPORTED_AGGS.MIN, @@ -58,6 +62,7 @@ export const pivotAggsFieldSupport = { PIVOT_SUPPORTED_AGGS.SUM, PIVOT_SUPPORTED_AGGS.VALUE_COUNT, PIVOT_SUPPORTED_AGGS.FILTER, + PIVOT_SUPPORTED_AGGS.TERMS, PIVOT_SUPPORTED_AGGS.TOP_METRICS, ], [KBN_FIELD_TYPES.STRING]: [ diff --git a/x-pack/plugins/transform/public/app/common/pivot_group_by.ts b/x-pack/plugins/transform/public/app/common/pivot_group_by.ts index 04f524a3f4163..9b557140d222b 100644 --- a/x-pack/plugins/transform/public/app/common/pivot_group_by.ts +++ b/x-pack/plugins/transform/public/app/common/pivot_group_by.ts @@ -27,13 +27,16 @@ export type PivotSupportedGroupByAggsWithInterval = export const pivotGroupByFieldSupport = { [KBN_FIELD_TYPES.ATTACHMENT]: [], - [KBN_FIELD_TYPES.BOOLEAN]: [], + [KBN_FIELD_TYPES.BOOLEAN]: [PIVOT_SUPPORTED_GROUP_BY_AGGS.TERMS], [KBN_FIELD_TYPES.DATE]: [PIVOT_SUPPORTED_GROUP_BY_AGGS.DATE_HISTOGRAM], [KBN_FIELD_TYPES.GEO_POINT]: [], [KBN_FIELD_TYPES.GEO_SHAPE]: [], [KBN_FIELD_TYPES.IP]: [PIVOT_SUPPORTED_GROUP_BY_AGGS.TERMS], [KBN_FIELD_TYPES.MURMUR3]: [], - [KBN_FIELD_TYPES.NUMBER]: [PIVOT_SUPPORTED_GROUP_BY_AGGS.HISTOGRAM], + [KBN_FIELD_TYPES.NUMBER]: [ + PIVOT_SUPPORTED_GROUP_BY_AGGS.HISTOGRAM, + PIVOT_SUPPORTED_GROUP_BY_AGGS.TERMS, + ], [KBN_FIELD_TYPES.STRING]: [PIVOT_SUPPORTED_GROUP_BY_AGGS.TERMS], [KBN_FIELD_TYPES._SOURCE]: [], [KBN_FIELD_TYPES.UNKNOWN]: [], diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/common.test.ts b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/common.test.ts index e99f06a2d0222..9a816ef1fe111 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/common.test.ts +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/common.test.ts @@ -6,7 +6,7 @@ */ import { getPivotDropdownOptions } from '.'; -import { DataView } from '@kbn/data-views-plugin/public'; +import type { DataView } from '@kbn/data-views-plugin/public'; import { FilterAggForm } from './filter_agg/components'; import type { RuntimeField } from '@kbn/data-views-plugin/common'; import { PercentilesAggForm } from './percentiles_agg/percentiles_form_component'; @@ -31,8 +31,8 @@ describe('Transform: Define Pivot Common', () => { } as DataView; const options = getPivotDropdownOptions(dataView); - expect(options).toMatchObject({ + fields: [{ name: ' the-f[i]e>ld ', type: 'number' }], aggOptions: [ { label: ' the-f[i]e>ld ', @@ -45,8 +45,10 @@ describe('Transform: Define Pivot Common', () => { { label: 'sum( the-f[i]e>ld )' }, { label: 'value_count( the-f[i]e>ld )' }, { label: 'filter( the-f[i]e>ld )' }, + { label: 'terms( the-f[i]e>ld )' }, { label: 'top_metrics( the-f[i]e>ld )' }, ], + field: { id: ' the-f[i]e>ld ', type: 'number' }, }, ], aggOptionsData: { @@ -101,8 +103,29 @@ describe('Transform: Define Pivot Common', () => { aggName: 'the-field.value_count', dropDownName: 'value_count( the-f[i]e>ld )', }, + 'terms( the-f[i]e>ld )': { + agg: 'terms', + field: ' the-f[i]e>ld ', + aggName: 'the-field.terms', + dropDownName: 'terms( the-f[i]e>ld )', + isSubAggsSupported: false, + isMultiField: false, + aggConfig: { size: 10 }, + }, + 'top_metrics( the-f[i]e>ld )': { + agg: 'top_metrics', + field: ' the-f[i]e>ld ', + aggName: 'top_metrics', + dropDownName: 'top_metrics( the-f[i]e>ld )', + isSubAggsSupported: false, + isMultiField: true, + aggConfig: {}, + }, }, - groupByOptions: [{ label: 'histogram( the-f[i]e>ld )' }], + groupByOptions: [ + { label: 'histogram( the-f[i]e>ld )', field: { id: ' the-f[i]e>ld ', type: 'number' } }, + { label: 'terms( the-f[i]e>ld )', field: { id: ' the-f[i]e>ld ', type: 'number' } }, + ], groupByOptionsData: { 'histogram( the-f[i]e>ld )': { agg: 'histogram', @@ -111,6 +134,12 @@ describe('Transform: Define Pivot Common', () => { dropDownName: 'histogram( the-f[i]e>ld )', interval: '10', }, + 'terms( the-f[i]e>ld )': { + agg: 'terms', + aggName: 'the-field', + dropDownName: 'terms( the-f[i]e>ld )', + field: ' the-f[i]e>ld ', + }, }, }); @@ -124,6 +153,10 @@ describe('Transform: Define Pivot Common', () => { }; const optionsWithRuntimeFields = getPivotDropdownOptions(dataView, runtimeMappings); expect(optionsWithRuntimeFields).toMatchObject({ + fields: [ + { name: ' the-f[i]e>ld ', type: 'number' }, + { name: 'rt_bytes_bigger', type: 'number' }, + ], aggOptions: [ { label: ' the-f[i]e>ld ', @@ -136,8 +169,10 @@ describe('Transform: Define Pivot Common', () => { { label: 'sum( the-f[i]e>ld )' }, { label: 'value_count( the-f[i]e>ld )' }, { label: 'filter( the-f[i]e>ld )' }, + { label: 'terms( the-f[i]e>ld )' }, { label: 'top_metrics( the-f[i]e>ld )' }, ], + field: { id: ' the-f[i]e>ld ', type: 'number' }, }, { label: 'rt_bytes_bigger', @@ -150,8 +185,10 @@ describe('Transform: Define Pivot Common', () => { { label: 'sum(rt_bytes_bigger)' }, { label: 'value_count(rt_bytes_bigger)' }, { label: 'filter(rt_bytes_bigger)' }, + { label: 'terms(rt_bytes_bigger)' }, { label: 'top_metrics(rt_bytes_bigger)' }, ], + field: { id: 'rt_bytes_bigger', type: 'number' }, }, ], aggOptionsData: { @@ -207,6 +244,24 @@ describe('Transform: Define Pivot Common', () => { isSubAggsSupported: true, AggFormComponent: FilterAggForm, }, + 'terms( the-f[i]e>ld )': { + agg: 'terms', + aggName: 'the-field.terms', + dropDownName: 'terms( the-f[i]e>ld )', + field: ' the-f[i]e>ld ', + isSubAggsSupported: false, + isMultiField: false, + aggConfig: { size: 10 }, + }, + 'top_metrics( the-f[i]e>ld )': { + agg: 'top_metrics', + aggName: 'top_metrics', + dropDownName: 'top_metrics( the-f[i]e>ld )', + field: ' the-f[i]e>ld ', + isSubAggsSupported: false, + isMultiField: true, + aggConfig: {}, + }, 'avg(rt_bytes_bigger)': { agg: 'avg', aggName: 'rt_bytes_bigger.avg', @@ -259,10 +314,30 @@ describe('Transform: Define Pivot Common', () => { isSubAggsSupported: true, AggFormComponent: FilterAggForm, }, + 'terms(rt_bytes_bigger)': { + agg: 'terms', + field: 'rt_bytes_bigger', + aggName: 'rt_bytes_bigger.terms', + dropDownName: 'terms(rt_bytes_bigger)', + isSubAggsSupported: false, + isMultiField: false, + aggConfig: { size: 10 }, + }, + 'top_metrics(rt_bytes_bigger)': { + agg: 'top_metrics', + field: 'rt_bytes_bigger', + aggName: 'top_metrics', + dropDownName: 'top_metrics(rt_bytes_bigger)', + isSubAggsSupported: false, + isMultiField: true, + aggConfig: {}, + }, }, groupByOptions: [ - { label: 'histogram( the-f[i]e>ld )' }, - { label: 'histogram(rt_bytes_bigger)' }, + { label: 'histogram( the-f[i]e>ld )', field: { id: ' the-f[i]e>ld ', type: 'number' } }, + { label: 'terms( the-f[i]e>ld )', field: { id: ' the-f[i]e>ld ', type: 'number' } }, + { label: 'histogram(rt_bytes_bigger)', field: { id: 'rt_bytes_bigger', type: 'number' } }, + { label: 'terms(rt_bytes_bigger)', field: { id: 'rt_bytes_bigger', type: 'number' } }, ], groupByOptionsData: { 'histogram( the-f[i]e>ld )': { @@ -272,6 +347,12 @@ describe('Transform: Define Pivot Common', () => { field: ' the-f[i]e>ld ', interval: '10', }, + 'terms( the-f[i]e>ld )': { + agg: 'terms', + field: ' the-f[i]e>ld ', + aggName: 'the-field', + dropDownName: 'terms( the-f[i]e>ld )', + }, 'histogram(rt_bytes_bigger)': { agg: 'histogram', aggName: 'rt_bytes_bigger', @@ -279,6 +360,12 @@ describe('Transform: Define Pivot Common', () => { field: 'rt_bytes_bigger', interval: '10', }, + 'terms(rt_bytes_bigger)': { + agg: 'terms', + field: 'rt_bytes_bigger', + aggName: 'rt_bytes_bigger', + dropDownName: 'terms(rt_bytes_bigger)', + }, }, }); }); diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx index e729b45342fb2..3aafa037b487d 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx @@ -304,7 +304,7 @@ export const StepDefineForm: FC = React.memo((props) => { 'xpack.transform.stepDefineForm.datePickerIconTipContent', { defaultMessage: - 'The time range will be applied to previews only and will not be part of the final transform configuration.', + 'The time range is applied to previews only and will not be part of the final transform configuration.', } )} /> diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/transform_function_selector.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/transform_function_selector.tsx index 6fbca6423c1cc..3aec137a3adf8 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/transform_function_selector.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/transform_function_selector.tsx @@ -23,7 +23,7 @@ export const TransformFunctionSelector: FC = ({ { name: TRANSFORM_FUNCTION.PIVOT, helpText: i18n.translate('xpack.transform.stepDefineForm.pivotHelperText', { - defaultMessage: 'Aggregate and group your data', + defaultMessage: 'Aggregate and group your data.', }), icon: 'aggregate', title: i18n.translate('xpack.transform.stepDefineForm.pivotLabel', { @@ -33,7 +33,7 @@ export const TransformFunctionSelector: FC = ({ { name: TRANSFORM_FUNCTION.LATEST, helpText: i18n.translate('xpack.transform.stepDefineForm.latestHelperText', { - defaultMessage: 'Keep track of your most recent data', + defaultMessage: 'Keep track of your most recent data.', }), icon: 'clock', title: i18n.translate('xpack.transform.stepDefineForm.latestLabel', { diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx index edad7b2932151..bc3f6b7fc5009 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx @@ -793,7 +793,7 @@ export const StepDetailsForm: FC = React.memo( } helpText={i18n.translate('xpack.transform.stepDetailsForm.frequencyHelpText', { defaultMessage: - 'The interval to check for changes in source indices when the transformation runs continuously.', + 'The interval to check for changes in source indices when the transform runs continuously.', })} > = ({ errorMessages={formFields.frequency.errorMessages} helpText={i18n.translate('xpack.transform.transformList.editFlyoutFormFrequencyHelpText', { defaultMessage: - 'The interval to check for changes in source indices when the transformation runs continuously.', + 'The interval to check for changes in source indices when the transform runs continuously.', })} label={i18n.translate('xpack.transform.transformList.editFlyoutFormFrequencyLabel', { defaultMessage: 'Frequency', diff --git a/x-pack/plugins/transform/readme.md b/x-pack/plugins/transform/readme.md index 8a0ea7eb4f660..7eb1f59250be9 100644 --- a/x-pack/plugins/transform/readme.md +++ b/x-pack/plugins/transform/readme.md @@ -104,33 +104,45 @@ Run the following commands from the `x-pack` directory and use separate terminal for test server and test runner. The test server command starts an Elasticsearch and Kibana instance that the tests will be run against. -1. Functional UI tests with `Trial` license (default config): +Functional tests are broken up into independent groups with their own configuration. +Test server and runner need to be pointed to the configuration to run. The basic +commands are - node scripts/functional_tests_server.js --config test/functional/apps/transform/config.ts - node scripts/functional_test_runner.js --config test/functional/apps/transform/config.ts + node scripts/functional_tests_server.js --config PATH_TO_CONFIG + node scripts/functional_test_runner.js --config PATH_TO_CONFIG - Transform functional `Trial` license tests are located in `x-pack/test/functional/apps/transform`. +With PATH_TO_CONFIG and other options as follows. -1. Functional UI tests with `Basic` license: +1. Functional UI tests with `Trial` license: - node scripts/functional_tests_server.js --config test/functional_basic/config.ts - node scripts/functional_test_runner.js --config test/functional_basic/config.ts --include-tag transform + Group | PATH_TO_CONFIG + ----- | -------------- + creation - index pattern | `test/functional/apps/transform/creation/index_pattern/config.ts` + creation - runtime mappings, saved searches | `test/functional/apps/transform/creation/runtime_mappings_saved_search/config.ts` + edit, clone | `test/functional/apps/transform/edit_clone/config.ts` + feature controls | `test/functional/apps/transform/feature_controls/config.ts` + permissions | `test/functional/apps/transform/permissions/config.ts` + start, reset, delete | `test/functional/apps/transform/start_reset_delete/config.ts` - Transform functional `Basic` license tests are located in `x-pack/test/functional_basic/apps/transform`. +1. Functional UI tests with `Basic` license: + + Group | PATH_TO_CONFIG + ----- | -------------- + creation - index pattern | `test/functional_basic/apps/transform/creation/index_pattern/config.ts` + creation - runtime mappings, saved searches | `test/functional_basic/apps/transform/creation/runtime_mappings_saved_search/config.ts` + edit, clone | `test/functional_basic/apps/transform/edit_clone/config.ts` + feature controls | `test/functional_basic/apps/transform/feature_controls/config.ts` + permissions | `test/functional_basic/apps/transform/permissions/config.ts` + start, reset, delete | `test/functional_basic/apps/transform/start_reset_delete/config.ts` 1. API integration tests with `Trial` license: - node scripts/functional_tests_server.js --config test/api_integration/config.ts - node scripts/functional_test_runner.js --config test/api_integration/config.ts --include-tag transform - - Transform API integration `Trial` license tests are located in `x-pack/test/api_integration/apis/transform`. + - PATH_TO_CONFIG: `test/api_integration/apis/transform/config.ts` 1. API integration tests with `Basic` license: - node scripts/functional_tests_server.js --config test/api_integration_basic/config.ts - node scripts/functional_test_runner.js --config test/api_integration_basic/config.ts --include-tag transform - - Transform API integration `Basic` license tests are located in `x-pack/test/api_integration_basic/apis/ml`. + - PATH_TO_CONFIG: `test/api_integration_basic/config.ts` + - Add `--include-tag transform` to the test runner command 1. Accessibility tests: diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 418dae07fcc58..516eef2d0157e 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -4480,10 +4480,8 @@ "presentationUtil.labs.components.disabledStatusMessage": "Par défaut : {status}", "presentationUtil.labs.components.enabledStatusMessage": "Par défaut : {status}", "presentationUtil.labs.components.noProjectsinSolutionMessage": "Aucun atelier actuellement dans {solutionName}.", - "presentationUtil.solutionToolbar.quickButton.ariaButtonLabel": "Créer {createType}", "presentationUtil.dashboardPicker.searchDashboardPlaceholder": "Recherche dans les tableaux de bord…", "presentationUtil.dataViewPicker.changeDataViewTitle": "Vue de données", - "presentationUtil.fieldPicker.noDataViewLabel": "Aucune vue de données sélectionnée", "presentationUtil.fieldPicker.noFieldsLabel": "Aucun champ correspondant", "presentationUtil.fieldSearch.fieldFilterButtonLabel": "Filtrer par type", "presentationUtil.fieldSearch.filterByTypeLabel": "Filtrer par type", @@ -4518,9 +4516,6 @@ "presentationUtil.saveModalDashboard.saveAndGoToDashboardLabel": "Enregistrer et accéder au tableau de bord", "presentationUtil.saveModalDashboard.saveLabel": "Enregistrer", "presentationUtil.saveModalDashboard.saveToLibraryLabel": "Enregistrer et ajouter à la bibliothèque", - "presentationUtil.solutionToolbar.editorMenuButtonLabel": "Tous les éditeurs", - "presentationUtil.solutionToolbar.libraryButtonLabel": "Ajouter depuis la bibliothèque", - "presentationUtil.solutionToolbar.quickButton.legendLabel": "Création rapide", "savedObjects.confirmModal.overwriteConfirmationMessage": "Êtes-vous sûr de vouloir écraser {title} ?", "savedObjects.confirmModal.overwriteTitle": "Écraser {name} ?", "savedObjects.confirmModal.saveDuplicateButtonLabel": "Enregistrer {name}", @@ -9647,15 +9642,9 @@ "xpack.cases.markdownEditor.plugins.lens.insertLensSavedObjectModal.searchSelection.notFoundLabel": "Impossible de trouver un Lens correspondant.", "xpack.cases.markdownEditor.plugins.lens.insertLensSavedObjectModal.searchSelection.savedObjectType.lens": "Lens", "xpack.cases.markdownEditor.plugins.lens.openVisualizationButtonLabel": "Visualisation ouverte", - "xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.filterButtonLabel": "Types", "xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.searchInputHelpText": "Insérez un Lens à partir de modèles existants ou en créant un nouveau modèle. Vous créerez un Lens uniquement pour ce commentaire et ne changerez pas la Bibliothèque Visualize.", "xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.searchInputLabel": "Sélectionner un Lens", "xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.searchInputPrependLabel": "Modèle", - "xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.searchPlaceholder": "Rechercher…", - "xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.sortAsc": "Croissant", - "xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.sortAuto": "Meilleure correspondance", - "xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.sortButtonLabel": "Trier", - "xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.sortDesc": "Décroissant", "xpack.cases.markdownEditor.plugins.lens.visualizationButtonLabel": "Visualisation", "xpack.cases.markdownEditor.plugins.timeline.noParenthesesErrorMsg": "Parenthèses gauches attendues", "xpack.cases.markdownEditor.preview": "Aperçu", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index e7fb5367a505c..c81197b7eba19 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -4478,10 +4478,8 @@ "presentationUtil.labs.components.disabledStatusMessage": "デフォルト: {status}", "presentationUtil.labs.components.enabledStatusMessage": "デフォルト: {status}", "presentationUtil.labs.components.noProjectsinSolutionMessage": "現在{solutionName}にはラボがありません。", - "presentationUtil.solutionToolbar.quickButton.ariaButtonLabel": "新しい{createType}を作成", "presentationUtil.dashboardPicker.searchDashboardPlaceholder": "ダッシュボードを検索...", "presentationUtil.dataViewPicker.changeDataViewTitle": "データビュー", - "presentationUtil.fieldPicker.noDataViewLabel": "データビューが選択されていません", "presentationUtil.fieldPicker.noFieldsLabel": "一致するがフィールドがありません", "presentationUtil.fieldSearch.fieldFilterButtonLabel": "タイプでフィルタリング", "presentationUtil.fieldSearch.filterByTypeLabel": "タイプでフィルタリング", @@ -4516,9 +4514,6 @@ "presentationUtil.saveModalDashboard.saveAndGoToDashboardLabel": "保存してダッシュボードを開く", "presentationUtil.saveModalDashboard.saveLabel": "保存", "presentationUtil.saveModalDashboard.saveToLibraryLabel": "保存してライブラリに追加", - "presentationUtil.solutionToolbar.editorMenuButtonLabel": "すべてのエディター", - "presentationUtil.solutionToolbar.libraryButtonLabel": "ライブラリから追加", - "presentationUtil.solutionToolbar.quickButton.legendLabel": "クイック作成", "savedObjects.confirmModal.overwriteConfirmationMessage": "{title}を上書きしてよろしいですか?", "savedObjects.confirmModal.overwriteTitle": "{name} を上書きしますか?", "savedObjects.confirmModal.saveDuplicateButtonLabel": "{name} を保存", @@ -9636,15 +9631,9 @@ "xpack.cases.markdownEditor.plugins.lens.insertLensSavedObjectModal.searchSelection.notFoundLabel": "一致するLensが見つかりません。", "xpack.cases.markdownEditor.plugins.lens.insertLensSavedObjectModal.searchSelection.savedObjectType.lens": "レンズ", "xpack.cases.markdownEditor.plugins.lens.openVisualizationButtonLabel": "ビジュアライゼーションを開く", - "xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.filterButtonLabel": "タイプ", "xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.searchInputHelpText": "既存のテンプレートからLensを挿入するか、新しく作成します。このコメントでのみLensが作成されます。Visualize Libraryは変更されません。", "xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.searchInputLabel": "Lensを選択", "xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.searchInputPrependLabel": "テンプレート", - "xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.searchPlaceholder": "検索…", - "xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.sortAsc": "昇順", - "xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.sortAuto": "ベストマッチ", - "xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.sortButtonLabel": "並べ替え", - "xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.sortDesc": "降順", "xpack.cases.markdownEditor.plugins.lens.visualizationButtonLabel": "ビジュアライゼーション", "xpack.cases.markdownEditor.plugins.timeline.noParenthesesErrorMsg": "想定される左括弧", "xpack.cases.markdownEditor.preview": "プレビュー", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 24783375ebe95..53f914a9e72e3 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -4483,10 +4483,8 @@ "presentationUtil.labs.components.disabledStatusMessage": "默认:{status}", "presentationUtil.labs.components.enabledStatusMessage": "默认:{status}", "presentationUtil.labs.components.noProjectsinSolutionMessage": "{solutionName} 中当前没有实验。", - "presentationUtil.solutionToolbar.quickButton.ariaButtonLabel": "创建新的 {createType}", "presentationUtil.dashboardPicker.searchDashboardPlaceholder": "搜索仪表板......", "presentationUtil.dataViewPicker.changeDataViewTitle": "数据视图", - "presentationUtil.fieldPicker.noDataViewLabel": "未选择数据视图", "presentationUtil.fieldPicker.noFieldsLabel": "无匹配字段", "presentationUtil.fieldSearch.fieldFilterButtonLabel": "按类型筛选", "presentationUtil.fieldSearch.filterByTypeLabel": "按类型筛选", @@ -4521,9 +4519,6 @@ "presentationUtil.saveModalDashboard.saveAndGoToDashboardLabel": "保存并前往仪表板", "presentationUtil.saveModalDashboard.saveLabel": "保存", "presentationUtil.saveModalDashboard.saveToLibraryLabel": "保存并添加到库", - "presentationUtil.solutionToolbar.editorMenuButtonLabel": "所有编辑器", - "presentationUtil.solutionToolbar.libraryButtonLabel": "从库中添加", - "presentationUtil.solutionToolbar.quickButton.legendLabel": "快速创建", "savedObjects.confirmModal.overwriteConfirmationMessage": "确定要覆盖“{title}”?", "savedObjects.confirmModal.overwriteTitle": "覆盖“{name}”?", "savedObjects.confirmModal.saveDuplicateButtonLabel": "保存“{name}”", @@ -9651,15 +9646,9 @@ "xpack.cases.markdownEditor.plugins.lens.insertLensSavedObjectModal.searchSelection.notFoundLabel": "未找到匹配的 lens。", "xpack.cases.markdownEditor.plugins.lens.insertLensSavedObjectModal.searchSelection.savedObjectType.lens": "Lens", "xpack.cases.markdownEditor.plugins.lens.openVisualizationButtonLabel": "打开可视化", - "xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.filterButtonLabel": "类型", "xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.searchInputHelpText": "通过现有模板或创建新模板来插入 Lens。您将仅为此注释创建 Lens,并且不会更改可视化库。", "xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.searchInputLabel": "选择 Lens", "xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.searchInputPrependLabel": "模板", - "xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.searchPlaceholder": "搜索……", - "xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.sortAsc": "升序", - "xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.sortAuto": "最佳匹配", - "xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.sortButtonLabel": "排序", - "xpack.cases.markdownEditor.plugins.lens.savedObjects.finder.sortDesc": "降序", "xpack.cases.markdownEditor.plugins.lens.visualizationButtonLabel": "可视化", "xpack.cases.markdownEditor.plugins.timeline.noParenthesesErrorMsg": "应为左括号", "xpack.cases.markdownEditor.preview": "预览", diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/json_editor_with_message_variables.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/json_editor_with_message_variables.test.tsx index 5349e60a60adb..0cac98f01f636 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/json_editor_with_message_variables.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/json_editor_with_message_variables.test.tsx @@ -69,4 +69,18 @@ describe('JsonEditorWithMessageVariables', () => { '{{{myVar}}}' ); }); + + test('renders correct value when the input value prop updates', () => { + const wrapper = mountWithIntl(); + + expect(wrapper.find('[data-test-subj="fooJsonEditor"]').first().prop('value')).toEqual(''); + + const inputTargetValue = '{"new": "value"}'; + wrapper.setProps({ inputTargetValue }); + wrapper.update(); + + expect(wrapper.find('[data-test-subj="fooJsonEditor"]').first().prop('value')).toEqual( + inputTargetValue + ); + }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/json_editor_with_message_variables.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/json_editor_with_message_variables.tsx index 643e1b69a513d..3485a99c39456 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/json_editor_with_message_variables.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/json_editor_with_message_variables.tsx @@ -37,7 +37,7 @@ interface Props { buttonTitle?: string; messageVariables?: ActionVariable[]; paramsProperty: string; - inputTargetValue?: string; + inputTargetValue?: string | null; label: string; errors?: string[]; areaLabel?: string; @@ -75,6 +75,13 @@ export const JsonEditorWithMessageVariables: React.FunctionComponent = ({ const { convertToJson, setXJson, xJson } = useXJsonMode(inputTargetValue ?? null); + useEffect(() => { + if (!xJson && inputTargetValue) { + setXJson(inputTargetValue); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [inputTargetValue]); + const onSelectMessageVariable = (variable: ActionVariable) => { const editor = editorRef.current; if (!editor) { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_edit_select.tsx b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_edit_select.tsx index e2410801dd5b4..84e762cbe93f8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_edit_select.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_edit_select.tsx @@ -95,7 +95,10 @@ export function useBulkEditSelect(props: UseBulkEditSelectProps) { searchText, } = props; - const [state, dispatch] = useReducer(reducer, initialState); + const [state, dispatch] = useReducer(reducer, { + ...initialState, + selectedIds: new Set(), + }); const itemIds = useMemo(() => { return items.map((item) => item.id); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx index 193d6abf1433b..6a170363d34a8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx @@ -7,6 +7,7 @@ import React, { useCallback, useState } from 'react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { Query, TimeRange } from '@kbn/es-query'; import { NO_INDEX_PATTERNS } from './constants'; import { SEARCH_BAR_PLACEHOLDER } from './translations'; import { AlertsSearchBarProps, QueryLanguageType } from './types'; @@ -32,16 +33,20 @@ export function AlertsSearchBar({ const { value: dataView, loading, error } = useAlertDataView(featureIds); const onQuerySubmit = useCallback( - (payload) => { - const { dateRange, query: nextQuery } = payload; + ({ dateRange, query: nextQuery }: { dateRange: TimeRange; query?: Query }) => { onQueryChange({ dateRange, - query: typeof nextQuery?.query === 'string' ? nextQuery.query : '', + query: typeof nextQuery?.query === 'string' ? nextQuery.query : undefined, }); setQueryLanguage((nextQuery?.language ?? 'kuery') as QueryLanguageType); }, [onQueryChange, setQueryLanguage] ); + const onRefresh = ({ dateRange }: { dateRange: TimeRange }) => { + onQueryChange({ + dateRange, + }); + }; return ( ); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/types.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/types.ts index e75dd56168687..2c94c250c168a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/types.ts @@ -17,6 +17,6 @@ export interface AlertsSearchBarProps { query?: string; onQueryChange: (query: { dateRange: { from: string; to: string; mode?: 'absolute' | 'relative' }; - query: string; + query?: string; }) => void; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/bulk_actions.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/bulk_actions.test.tsx index 27188fab622c9..3dc86c5522e8e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/bulk_actions.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/bulk_actions.test.tsx @@ -584,7 +584,8 @@ describe('AlertsTable.BulkActions', () => { }); }); - describe('and executing a bulk action', () => { + // FLAKY: https://github.com/elastic/kibana/issues/152176 + describe.skip('and executing a bulk action', () => { it('should return the are all selected flag set to true', async () => { const mockedFn = jest.fn(); const props = { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_bulk_delete.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_bulk_delete.test.tsx index 6e9276018f9b9..d7737d3af99d8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_bulk_delete.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_bulk_delete.test.tsx @@ -5,9 +5,17 @@ * 2.0. */ import * as React from 'react'; -import { ReactWrapper } from 'enzyme'; -import { act } from '@testing-library/react'; -import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; +import { IToasts } from '@kbn/core/public'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; +import { + render, + screen, + waitForElementToBeRemoved, + fireEvent, + act, + cleanup, +} from '@testing-library/react'; import { actionTypeRegistryMock } from '../../../action_type_registry.mock'; import { ruleTypeRegistryMock } from '../../../rule_type_registry.mock'; import { RulesList } from './rules_list'; @@ -19,7 +27,6 @@ import { getDisabledByLicenseRuleTypeFromApi, ruleType, } from './test_helpers'; -import { IToasts } from '@kbn/core/public'; jest.mock('../../../../common/lib/kibana'); jest.mock('@kbn/kibana-react-plugin/public/ui_settings/use_ui_setting', () => ({ @@ -85,7 +92,9 @@ jest.mock('../../../lib/capabilities', () => ({ jest.mock('../../../../common/get_experimental_features', () => ({ getIsExperimentalFeatureEnabled: jest.fn(), })); - +const { loadRuleAggregationsWithKueryFilter } = jest.requireMock( + '../../../lib/rule_api/aggregate_kuery_filter' +); const { loadRuleTypes } = jest.requireMock('../../../lib/rule_api/rule_types'); const { bulkDeleteRules } = jest.requireMock('../../../lib/rule_api/bulk_delete'); @@ -99,58 +108,40 @@ ruleTypeRegistry.list.mockReturnValue([ruleType]); actionTypeRegistry.list.mockReturnValue([]); const useKibanaMock = useKibana as jest.Mocked; - -beforeEach(() => { - (getIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(() => false); +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: false, + cacheTime: 0, + }, + }, }); -// Test are too slow. It's breaking the build. So we skipp it now and waiting for improvment according this ticket: -// https://github.com/elastic/kibana/issues/145122 -describe.skip('Rules list bulk delete', () => { - let wrapper: ReactWrapper; +const AllTheProviders = ({ children }: { children: any }) => ( + + {children} + +); + +const renderWithProviders = (ui: any) => { + return render(ui, { wrapper: AllTheProviders }); +}; - async function setup(authorized: boolean = true) { +describe('Rules list Bulk Delete', () => { + beforeEach(async () => { + (getIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(() => false); loadRulesWithKueryFilter.mockResolvedValue({ page: 1, perPage: 10000, total: 6, data: mockedRulesData, }); - - loadActionTypes.mockResolvedValue([ - { - id: 'test', - name: 'Test', - }, - { - id: 'test2', - name: 'Test2', - }, - ]); - loadRuleTypes.mockResolvedValue([ - ruleTypeFromApi, - getDisabledByLicenseRuleTypeFromApi(authorized), - ]); + loadActionTypes.mockResolvedValue([]); + loadRuleTypes.mockResolvedValue([ruleTypeFromApi, getDisabledByLicenseRuleTypeFromApi()]); loadAllActions.mockResolvedValue([]); - // eslint-disable-next-line react-hooks/rules-of-hooks + loadRuleAggregationsWithKueryFilter.mockResolvedValue({}); useKibanaMock().services.ruleTypeRegistry = ruleTypeRegistry; - - // eslint-disable-next-line react-hooks/rules-of-hooks useKibanaMock().services.actionTypeRegistry = actionTypeRegistry; - wrapper = mountWithIntl(); - - await act(async () => { - await nextTick(); - wrapper.update(); - }); - } - - afterEach(() => { - jest.clearAllMocks(); - }); - - beforeAll(async () => { - await setup(); useKibanaMock().services.notifications.toasts = { addSuccess: jest.fn(), addError: jest.fn(), @@ -159,22 +150,27 @@ describe.skip('Rules list bulk delete', () => { } as unknown as IToasts; }); - beforeEach(() => { - wrapper.find('[data-test-subj="checkboxSelectRow-1"]').at(1).simulate('change'); - wrapper.find('[data-test-subj="selectAllRulesButton"]').at(1).simulate('click'); - // Unselect something to test filtering - wrapper.find('[data-test-subj="checkboxSelectRow-2"]').at(1).simulate('change'); - wrapper.find('[data-test-subj="showBulkActionButton"]').first().simulate('click'); + afterEach(() => { + jest.clearAllMocks(); + queryClient.clear(); + cleanup(); }); - it('can bulk delete', async () => { - wrapper.find('button[data-test-subj="bulkDelete"]').first().simulate('click'); - expect(wrapper.find('[data-test-subj="rulesDeleteConfirmation"]').exists()).toBeTruthy(); - wrapper.find('button[data-test-subj="confirmModalConfirmButton"]').simulate('click'); + beforeEach(async () => { + renderWithProviders(); + await waitForElementToBeRemoved(() => screen.queryByTestId('centerJustifiedSpinner')); + fireEvent.click(screen.getByTestId('checkboxSelectRow-1')); + fireEvent.click(screen.getByTestId('selectAllRulesButton')); + fireEvent.click(screen.getByTestId('checkboxSelectRow-2')); + fireEvent.click(screen.getByTestId('showBulkActionButton')); + }); + + it('should Bulk Delete', async () => { + fireEvent.click(screen.getByTestId('bulkDelete')); + expect(screen.getByTestId('rulesDeleteConfirmation')).toBeInTheDocument(); await act(async () => { - await nextTick(); - wrapper.update(); + fireEvent.click(screen.getByTestId('confirmModalConfirmButton')); }); const filter = bulkDeleteRules.mock.calls[0][0].filter; @@ -192,96 +188,68 @@ describe.skip('Rules list bulk delete', () => { ); }); - it('can cancel bulk delete', async () => { - wrapper.find('[data-test-subj="bulkDelete"]').first().simulate('click'); - expect(wrapper.find('[data-test-subj="rulesDeleteConfirmation"]').exists()).toBeTruthy(); - wrapper.find('[data-test-subj="confirmModalCancelButton"]').first().simulate('click'); - + it('should cancel Bulk Delete', async () => { + fireEvent.click(screen.getByTestId('bulkDelete')); + expect(screen.getByTestId('rulesDeleteConfirmation')).toBeInTheDocument(); await act(async () => { - await nextTick(); - wrapper.update(); + fireEvent.click(screen.getByTestId('confirmModalCancelButton')); }); - expect(bulkDeleteRules).not.toBeCalled(); }); - describe('Toast', () => { - it('should have success toast message', async () => { - wrapper.find('button[data-test-subj="bulkDelete"]').first().simulate('click'); - expect(wrapper.find('[data-test-subj="rulesDeleteConfirmation"]').exists()).toBeTruthy(); - wrapper.find('button[data-test-subj="confirmModalConfirmButton"]').simulate('click'); - - await act(async () => { - await nextTick(); - wrapper.update(); - }); - - expect(useKibanaMock().services.notifications.toasts.addSuccess).toHaveBeenCalledTimes(1); - expect(useKibanaMock().services.notifications.toasts.addSuccess).toHaveBeenCalledWith( - 'Deleted 10 rules' - ); - }); - - it('should have warning toast message', async () => { - bulkDeleteRules.mockResolvedValue({ - errors: [ - { - message: 'string', - rule: { - id: 'string', - name: 'string', - }, + it('should have warning toast message after Bulk Delete', async () => { + bulkDeleteRules.mockResolvedValue({ + errors: [ + { + message: 'string', + rule: { + id: 'string', + name: 'string', }, - ], - total: 10, - }); - - wrapper.find('button[data-test-subj="bulkDelete"]').first().simulate('click'); - expect(wrapper.find('[data-test-subj="rulesDeleteConfirmation"]').exists()).toBeTruthy(); - wrapper.find('button[data-test-subj="confirmModalConfirmButton"]').simulate('click'); - - await act(async () => { - await nextTick(); - wrapper.update(); - }); - - expect(useKibanaMock().services.notifications.toasts.addWarning).toHaveBeenCalledTimes(1); - expect(useKibanaMock().services.notifications.toasts.addWarning).toHaveBeenCalledWith( - expect.objectContaining({ - title: 'Deleted 9 rules, 1 rule encountered errors', - }) - ); + }, + ], + total: 10, }); - it('should have danger toast message', async () => { - bulkDeleteRules.mockResolvedValue({ - errors: [ - { - message: 'string', - rule: { - id: 'string', - name: 'string', - }, - }, - ], - total: 1, - }); + fireEvent.click(screen.getByTestId('bulkDelete')); + expect(screen.getByTestId('rulesDeleteConfirmation')).toBeInTheDocument(); + await act(async () => { + fireEvent.click(screen.getByTestId('confirmModalConfirmButton')); + }); - wrapper.find('button[data-test-subj="bulkDelete"]').first().simulate('click'); - expect(wrapper.find('[data-test-subj="rulesDeleteConfirmation"]').exists()).toBeTruthy(); - wrapper.find('button[data-test-subj="confirmModalConfirmButton"]').simulate('click'); + expect(useKibanaMock().services.notifications.toasts.addWarning).toHaveBeenCalledTimes(1); + expect(useKibanaMock().services.notifications.toasts.addWarning).toHaveBeenCalledWith( + expect.objectContaining({ + title: 'Deleted 9 rules, 1 rule encountered errors', + }) + ); + }); - await act(async () => { - await nextTick(); - wrapper.update(); - }); + it('should have danger toast message after Bulk Delete', async () => { + bulkDeleteRules.mockResolvedValue({ + errors: [ + { + message: 'string', + rule: { + id: 'string', + name: 'string', + }, + }, + ], + total: 1, + }); - expect(useKibanaMock().services.notifications.toasts.addDanger).toHaveBeenCalledTimes(1); - expect(useKibanaMock().services.notifications.toasts.addDanger).toHaveBeenCalledWith( - expect.objectContaining({ - title: 'Failed to delete 1 rule', - }) - ); + fireEvent.click(screen.getByTestId('bulkDelete')); + expect(screen.getByTestId('rulesDeleteConfirmation')).toBeInTheDocument(); + await act(async () => { + fireEvent.click(screen.getByTestId('confirmModalConfirmButton')); }); + + expect(useKibanaMock().services.notifications.toasts.addDanger).toHaveBeenCalledTimes(1); + expect(useKibanaMock().services.notifications.toasts.addDanger).toHaveBeenCalledWith( + expect.objectContaining({ + title: 'Failed to delete 1 rule', + }) + ); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_bulk_disable.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_bulk_disable.test.tsx index eb3be548b59fe..0e6773ddd3092 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_bulk_disable.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_bulk_disable.test.tsx @@ -5,9 +5,17 @@ * 2.0. */ import * as React from 'react'; -import { ReactWrapper } from 'enzyme'; -import { act } from '@testing-library/react'; -import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; +import { IToasts } from '@kbn/core/public'; +import { + act, + render, + screen, + cleanup, + waitForElementToBeRemoved, + fireEvent, +} from '@testing-library/react'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { actionTypeRegistryMock } from '../../../action_type_registry.mock'; import { ruleTypeRegistryMock } from '../../../rule_type_registry.mock'; import { RulesList } from './rules_list'; @@ -19,7 +27,6 @@ import { getDisabledByLicenseRuleTypeFromApi, ruleType, } from './test_helpers'; -import { IToasts } from '@kbn/core/public'; jest.mock('../../../../common/lib/kibana'); jest.mock('@kbn/kibana-react-plugin/public/ui_settings/use_ui_setting', () => ({ @@ -30,7 +37,6 @@ jest.mock('../../../lib/action_connector_api', () => ({ loadActionTypes: jest.fn(), loadAllActions: jest.fn(), })); - jest.mock('../../../lib/rule_api/rules_kuery_filter', () => ({ loadRulesWithKueryFilter: jest.fn(), })); @@ -55,10 +61,8 @@ jest.mock('../../../lib/rule_api/health', () => ({ hasPermanentEncryptionKey: true, })), })); - jest.mock('../../../lib/rule_api/aggregate_kuery_filter'); jest.mock('../../../lib/rule_api/rules_kuery_filter'); - jest.mock('../../../../common/lib/health_api', () => ({ triggersActionsUiHealth: jest.fn(() => ({ isRulesAvailable: true })), })); @@ -75,7 +79,6 @@ jest.mock('react-router-dom', () => ({ pathname: '/triggersActions/rules/', }), })); - jest.mock('../../../lib/capabilities', () => ({ hasAllPrivilege: jest.fn(() => true), hasSaveRulesCapability: jest.fn(() => true), @@ -85,6 +88,13 @@ jest.mock('../../../lib/capabilities', () => ({ jest.mock('../../../../common/get_experimental_features', () => ({ getIsExperimentalFeatureEnabled: jest.fn(), })); +jest.mock('../../../lib/rule_api/aggregate_kuery_filter', () => ({ + loadRuleAggregationsWithKueryFilter: jest.fn(), +})); + +const { loadRuleAggregationsWithKueryFilter } = jest.requireMock( + '../../../lib/rule_api/aggregate_kuery_filter' +); const { loadRuleTypes } = jest.requireMock('../../../lib/rule_api/rule_types'); const { bulkDisableRules } = jest.requireMock('../../../lib/rule_api/bulk_disable'); @@ -99,58 +109,40 @@ ruleTypeRegistry.list.mockReturnValue([ruleType]); actionTypeRegistry.list.mockReturnValue([]); const useKibanaMock = useKibana as jest.Mocked; - -beforeEach(() => { - (getIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(() => false); +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: false, + cacheTime: 0, + }, + }, }); -// Test are too slow. It's breaking the build. So we skipp it now and waiting for improvment according this ticket: -// https://github.com/elastic/kibana/issues/145122 -describe.skip('Rules list bulk disable', () => { - let wrapper: ReactWrapper; +const AllTheProviders = ({ children }: { children: any }) => ( + + {children} + +); + +const renderWithProviders = (ui: any) => { + return render(ui, { wrapper: AllTheProviders }); +}; - const setup = async (authorized: boolean = true) => { +describe('Rules list Bulk Disable', () => { + beforeAll(async () => { + (getIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(() => false); loadRulesWithKueryFilter.mockResolvedValue({ page: 1, perPage: 10000, total: 6, data: mockedRulesData, }); - - loadActionTypes.mockResolvedValue([ - { - id: 'test', - name: 'Test', - }, - { - id: 'test2', - name: 'Test2', - }, - ]); - loadRuleTypes.mockResolvedValue([ - ruleTypeFromApi, - getDisabledByLicenseRuleTypeFromApi(authorized), - ]); + loadActionTypes.mockResolvedValue([]); + loadRuleTypes.mockResolvedValue([ruleTypeFromApi, getDisabledByLicenseRuleTypeFromApi()]); loadAllActions.mockResolvedValue([]); - // eslint-disable-next-line react-hooks/rules-of-hooks + loadRuleAggregationsWithKueryFilter.mockResolvedValue({}); useKibanaMock().services.ruleTypeRegistry = ruleTypeRegistry; - - // eslint-disable-next-line react-hooks/rules-of-hooks useKibanaMock().services.actionTypeRegistry = actionTypeRegistry; - wrapper = mountWithIntl(); - - await act(async () => { - await nextTick(); - wrapper.update(); - }); - }; - - afterEach(() => { - jest.clearAllMocks(); - }); - - beforeAll(async () => { - await setup(); useKibanaMock().services.notifications.toasts = { addSuccess: jest.fn(), addError: jest.fn(), @@ -159,20 +151,25 @@ describe.skip('Rules list bulk disable', () => { } as unknown as IToasts; }); - beforeEach(() => { - wrapper.find('[data-test-subj="checkboxSelectRow-1"]').at(1).simulate('change'); - wrapper.find('[data-test-subj="selectAllRulesButton"]').at(1).simulate('click'); - // Unselect something to test filtering - wrapper.find('[data-test-subj="checkboxSelectRow-2"]').at(1).simulate('change'); - wrapper.find('[data-test-subj="showBulkActionButton"]').first().simulate('click'); + afterEach(() => { + jest.clearAllMocks(); + queryClient.clear(); + cleanup(); }); - it.skip('can bulk disable', async () => { - wrapper.find('button[data-test-subj="bulkDisable"]').first().simulate('click'); + beforeEach(async () => { + renderWithProviders(); + await waitForElementToBeRemoved(() => screen.queryByTestId('centerJustifiedSpinner')); + + fireEvent.click(screen.getByTestId('checkboxSelectRow-1')); + fireEvent.click(screen.getByTestId('selectAllRulesButton')); + fireEvent.click(screen.getByTestId('checkboxSelectRow-2')); + fireEvent.click(screen.getByTestId('showBulkActionButton')); + }); + it('can bulk disable', async () => { await act(async () => { - await nextTick(); - wrapper.update(); + fireEvent.click(screen.getByTestId('bulkDisable')); }); const filter = bulkDisableRules.mock.calls[0][0].filter; @@ -188,19 +185,16 @@ describe.skip('Rules list bulk disable', () => { ids: [], }) ); - expect( - wrapper.find('[data-test-subj="checkboxSelectRow-1"]').first().prop('checked') - ).toBeFalsy(); - expect(wrapper.find('button[data-test-subj="bulkDisable"]').exists()).toBeFalsy(); + expect(screen.getByTestId('checkboxSelectRow-1').closest('tr')).not.toHaveClass( + 'euiTableRow-isSelected' + ); + expect(screen.queryByTestId('bulkDisable')).not.toBeInTheDocument(); }); describe('Toast', () => { it('should have success toast message', async () => { - wrapper.find('button[data-test-subj="bulkDisable"]').first().simulate('click'); - await act(async () => { - await nextTick(); - wrapper.update(); + fireEvent.click(screen.getByTestId('bulkDisable')); }); expect(useKibanaMock().services.notifications.toasts.addSuccess).toHaveBeenCalledTimes(1); @@ -223,11 +217,8 @@ describe.skip('Rules list bulk disable', () => { total: 10, }); - wrapper.find('button[data-test-subj="bulkDisable"]').first().simulate('click'); - await act(async () => { - await nextTick(); - wrapper.update(); + fireEvent.click(screen.getByTestId('bulkDisable')); }); expect(useKibanaMock().services.notifications.toasts.addWarning).toHaveBeenCalledTimes(1); @@ -252,11 +243,8 @@ describe.skip('Rules list bulk disable', () => { total: 1, }); - wrapper.find('button[data-test-subj="bulkDisable"]').first().simulate('click'); - await act(async () => { - await nextTick(); - wrapper.update(); + fireEvent.click(screen.getByTestId('bulkDisable')); }); expect(useKibanaMock().services.notifications.toasts.addDanger).toHaveBeenCalledTimes(1); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_bulk_edit.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_bulk_edit.test.tsx index 392ebdd8ca48c..271c12aa3f63b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_bulk_edit.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_bulk_edit.test.tsx @@ -5,9 +5,6 @@ * 2.0. */ import * as React from 'react'; -import { ReactWrapper } from 'enzyme'; -import { act } from '@testing-library/react'; -import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; import { actionTypeRegistryMock } from '../../../action_type_registry.mock'; import { ruleTypeRegistryMock } from '../../../rule_type_registry.mock'; import { RulesList } from './rules_list'; @@ -19,6 +16,10 @@ import { getDisabledByLicenseRuleTypeFromApi, ruleType, } from './test_helpers'; +import { IToasts } from '@kbn/core/public'; +import { act, render, screen, waitForElementToBeRemoved, fireEvent } from '@testing-library/react'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; jest.mock('../../../../common/lib/kibana'); jest.mock('@kbn/kibana-react-plugin/public/ui_settings/use_ui_setting', () => ({ @@ -29,7 +30,6 @@ jest.mock('../../../lib/action_connector_api', () => ({ loadActionTypes: jest.fn(), loadAllActions: jest.fn(), })); - jest.mock('../../../lib/rule_api/rules_kuery_filter', () => ({ loadRulesWithKueryFilter: jest.fn(), })); @@ -60,10 +60,8 @@ jest.mock('../../../lib/rule_api/health', () => ({ hasPermanentEncryptionKey: true, })), })); - jest.mock('../../../lib/rule_api/aggregate_kuery_filter'); jest.mock('../../../lib/rule_api/rules_kuery_filter'); - jest.mock('../../../../common/lib/health_api', () => ({ triggersActionsUiHealth: jest.fn(() => ({ isRulesAvailable: true })), })); @@ -80,7 +78,6 @@ jest.mock('react-router-dom', () => ({ pathname: '/triggersActions/rules/', }), })); - jest.mock('../../../lib/capabilities', () => ({ hasAllPrivilege: jest.fn(() => true), hasSaveRulesCapability: jest.fn(() => true), @@ -90,7 +87,13 @@ jest.mock('../../../lib/capabilities', () => ({ jest.mock('../../../../common/get_experimental_features', () => ({ getIsExperimentalFeatureEnabled: jest.fn(), })); +jest.mock('../../../lib/rule_api/aggregate_kuery_filter', () => ({ + loadRuleAggregationsWithKueryFilter: jest.fn(), +})); +const { loadRuleAggregationsWithKueryFilter } = jest.requireMock( + '../../../lib/rule_api/aggregate_kuery_filter' +); const { loadRuleTypes } = jest.requireMock('../../../lib/rule_api/rule_types'); const { bulkSnoozeRules } = jest.requireMock('../../../lib/rule_api/snooze'); const { bulkUnsnoozeRules } = jest.requireMock('../../../lib/rule_api/unsnooze'); @@ -106,116 +109,75 @@ ruleTypeRegistry.list.mockReturnValue([ruleType]); actionTypeRegistry.list.mockReturnValue([]); const useKibanaMock = useKibana as jest.Mocked; - -beforeEach(() => { - (getIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(() => false); +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: false, + cacheTime: 0, + }, + }, }); -describe.skip('Rules list bulk actions', () => { - let wrapper: ReactWrapper; +const AllTheProviders = ({ children }: { children: any }) => ( + + {children} + +); + +const renderWithProviders = (ui: any) => { + return render(ui, { wrapper: AllTheProviders }); +}; - async function setup(authorized: boolean = true) { +describe('Rules list Bulk Edit', () => { + beforeAll(async () => { + (getIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(() => false); loadRulesWithKueryFilter.mockResolvedValue({ page: 1, perPage: 10000, total: 6, data: mockedRulesData, }); - - loadActionTypes.mockResolvedValue([ - { - id: 'test', - name: 'Test', - }, - { - id: 'test2', - name: 'Test2', - }, - ]); - loadRuleTypes.mockResolvedValue([ - ruleTypeFromApi, - getDisabledByLicenseRuleTypeFromApi(authorized), - ]); + loadActionTypes.mockResolvedValue([]); + loadRuleTypes.mockResolvedValue([ruleTypeFromApi, getDisabledByLicenseRuleTypeFromApi()]); loadAllActions.mockResolvedValue([]); - // eslint-disable-next-line react-hooks/rules-of-hooks + loadRuleAggregationsWithKueryFilter.mockResolvedValue({}); useKibanaMock().services.ruleTypeRegistry = ruleTypeRegistry; - - // eslint-disable-next-line react-hooks/rules-of-hooks useKibanaMock().services.actionTypeRegistry = actionTypeRegistry; - wrapper = mountWithIntl(); - - await act(async () => { - await nextTick(); - wrapper.update(); - }); - } + useKibanaMock().services.notifications.toasts = { + addSuccess: jest.fn(), + addError: jest.fn(), + addDanger: jest.fn(), + addWarning: jest.fn(), + } as unknown as IToasts; + }); afterEach(() => { jest.clearAllMocks(); + queryClient.clear(); }); - it('renders select all button for bulk editing', async () => { - await setup(); - expect(wrapper.find('[data-test-subj="totalRulesCount"]').exists()).toBeTruthy(); - expect(wrapper.find('[data-test-subj="showBulkActionButton"]').exists()).toBeFalsy(); - expect(wrapper.find('[data-test-subj="selectAllRulesButton"]').exists()).toBeFalsy(); - - wrapper.find('[data-test-subj="checkboxSelectRow-1"]').at(1).simulate('change'); - - expect(wrapper.find('[data-test-subj="totalRulesCount"]').exists()).toBeFalsy(); - expect(wrapper.find('[data-test-subj="showBulkActionButton"]').exists()).toBeTruthy(); - expect(wrapper.find('[data-test-subj="selectAllRulesButton"]').exists()).toBeTruthy(); - }); - - it('does not render select all button if the user is not authorized', async () => { - await setup(false); - wrapper.find('[data-test-subj="checkboxSelectRow-1"]').at(1).simulate('change'); - expect(wrapper.find('[data-test-subj="showBulkActionButton"]').exists()).toBeTruthy(); - expect(wrapper.find('[data-test-subj="selectAllRulesButton"]').exists()).toBeFalsy(); - }); - - it('selects all will select all items', async () => { - await setup(); - wrapper.find('[data-test-subj="checkboxSelectRow-1"]').at(1).simulate('change'); - wrapper.find('[data-test-subj="selectAllRulesButton"]').at(1).simulate('click'); - - mockedRulesData.forEach((rule) => { - expect( - wrapper.find(`[data-test-subj="checkboxSelectRow-${rule.id}"]`).first().prop('checked') - ).toBeTruthy(); + // FLAKY: https://github.com/elastic/kibana/issues/152268 + // FLAKY: https://github.com/elastic/kibana/issues/152267 + describe.skip('bulk actions', () => { + beforeEach(async () => { + renderWithProviders(); + await waitForElementToBeRemoved(() => screen.queryByTestId('centerJustifiedSpinner')); + + fireEvent.click(screen.getByTestId('checkboxSelectRow-1')); + fireEvent.click(screen.getByTestId('selectAllRulesButton')); + fireEvent.click(screen.getByTestId('checkboxSelectRow-2')); + fireEvent.click(screen.getByTestId('showBulkActionButton')); }); - wrapper.find('[data-test-subj="showBulkActionButton"]').first().simulate('click'); - - expect(wrapper.find('[data-test-subj="ruleQuickEditButton"]').exists()).toBeTruthy(); - expect(wrapper.find('[data-test-subj="disableAll"]').first().prop('isDisabled')).toBeTruthy(); - expect(wrapper.find('[data-test-subj="bulkDelete"]').exists()).toBeTruthy(); - }); - - describe('bulk actions', () => { - beforeAll(async () => { - await setup(); - }); - - beforeEach(() => { - wrapper.find('[data-test-subj="checkboxSelectRow-1"]').at(1).simulate('change'); - wrapper.find('[data-test-subj="selectAllRulesButton"]').at(1).simulate('click'); - // Unselect something to test filtering - wrapper.find('[data-test-subj="checkboxSelectRow-2"]').at(1).simulate('change'); - wrapper.find('[data-test-subj="showBulkActionButton"]').first().simulate('click'); - }); - - it('can bulk snooze', async () => { - wrapper.find('[data-test-subj="bulkSnooze"]').first().simulate('click'); - expect(wrapper.find('[data-test-subj="snoozePanel"]').exists()).toBeTruthy(); - wrapper.find('[data-test-subj="linkSnooze1h"]').first().simulate('click'); - + it('can bulk add snooze schedule', async () => { + fireEvent.click(screen.getByTestId('bulkSnoozeSchedule')); + expect(screen.queryByTestId('ruleSnoozeScheduler')).toBeInTheDocument(); await act(async () => { - await nextTick(); - wrapper.update(); + fireEvent.click(screen.getByTestId('scheduler-saveSchedule')); }); const filter = bulkSnoozeRules.mock.calls[0][0].filter; + expect(filter.function).toEqual('and'); expect(filter.arguments[0].function).toEqual('or'); expect(filter.arguments[1].function).toEqual('not'); @@ -229,22 +191,11 @@ describe.skip('Rules list bulk actions', () => { ); }); - it('can bulk unsnooze', async () => { - wrapper.find('[data-test-subj="bulkUnsnooze"]').hostNodes().first().simulate('click'); - - expect( - wrapper.find('[data-test-subj="bulkUnsnoozeConfirmationModal"]').exists() - ).toBeTruthy(); - - wrapper - .find('[data-test-subj="confirmModalConfirmButton"]') - .hostNodes() - .first() - .simulate('click'); - + it('can bulk remove snooze schedule', async () => { + fireEvent.click(screen.getByTestId('bulkRemoveSnoozeSchedule')); + expect(screen.queryByTestId('bulkRemoveScheduleConfirmationModal')).toBeInTheDocument(); await act(async () => { - await nextTick(); - wrapper.update(); + fireEvent.click(screen.getByTestId('confirmModalConfirmButton')); }); const filter = bulkUnsnoozeRules.mock.calls[0][0].filter; @@ -258,21 +209,16 @@ describe.skip('Rules list bulk actions', () => { expect(bulkUnsnoozeRules).toHaveBeenCalledWith( expect.objectContaining({ ids: [], + scheduleIds: [], }) ); }); - it('can bulk add snooze schedule', async () => { - wrapper.find('[data-test-subj="bulkSnoozeSchedule"]').hostNodes().first().simulate('click'); - expect(wrapper.find('[data-test-subj="ruleSnoozeScheduler"]').exists()).toBeTruthy(); - wrapper - .find('[data-test-subj="scheduler-saveSchedule"]') - .hostNodes() - .first() - .simulate('click'); + it('can bulk snooze', async () => { + fireEvent.click(screen.getByTestId('bulkSnooze')); + expect(screen.queryByTestId('snoozePanel')).toBeInTheDocument(); await act(async () => { - await nextTick(); - wrapper.update(); + fireEvent.click(screen.getByTestId('linkSnooze1h')); }); const filter = bulkSnoozeRules.mock.calls[0][0].filter; @@ -290,24 +236,11 @@ describe.skip('Rules list bulk actions', () => { ); }); - it('can bulk remove snooze schedule', async () => { - wrapper - .find('[data-test-subj="bulkRemoveSnoozeSchedule"]') - .hostNodes() - .first() - .simulate('click'); - expect( - wrapper.find('[data-test-subj="bulkRemoveScheduleConfirmationModal"]').exists() - ).toBeTruthy(); - wrapper - .find('[data-test-subj="confirmModalConfirmButton"]') - .hostNodes() - .first() - .simulate('click'); - + it('can bulk unsnooze', async () => { + fireEvent.click(screen.getByTestId('bulkUnsnooze')); + expect(screen.queryByTestId('bulkUnsnoozeConfirmationModal')).toBeInTheDocument(); await act(async () => { - await nextTick(); - wrapper.update(); + fireEvent.click(screen.getByTestId('confirmModalConfirmButton')); }); const filter = bulkUnsnoozeRules.mock.calls[0][0].filter; @@ -321,22 +254,15 @@ describe.skip('Rules list bulk actions', () => { expect(bulkUnsnoozeRules).toHaveBeenCalledWith( expect.objectContaining({ ids: [], - scheduleIds: [], }) ); }); it('can bulk update API key', async () => { - wrapper.find('[data-test-subj="updateAPIKeys"]').hostNodes().first().simulate('click'); - expect(wrapper.find('[data-test-subj="updateApiKeyIdsConfirmation"]').exists()).toBeTruthy(); - wrapper - .find('[data-test-subj="confirmModalConfirmButton"]') - .hostNodes() - .first() - .simulate('click'); + fireEvent.click(screen.getByTestId('updateAPIKeys')); + expect(screen.queryByTestId('updateApiKeyIdsConfirmation')).toBeInTheDocument(); await act(async () => { - await nextTick(); - wrapper.update(); + fireEvent.click(screen.getByTestId('confirmModalConfirmButton')); }); const filter = bulkUpdateAPIKey.mock.calls[0][0].filter; @@ -354,4 +280,51 @@ describe.skip('Rules list bulk actions', () => { ); }); }); + + it('renders select all button for bulk editing', async () => { + renderWithProviders(); + await waitForElementToBeRemoved(() => screen.queryByTestId('centerJustifiedSpinner')); + + expect(screen.queryByTestId('totalRulesCount')).toBeInTheDocument(); + expect(screen.queryByTestId('showBulkActionButton')).not.toBeInTheDocument(); + expect(screen.queryByTestId('selectAllRulesButton')).not.toBeInTheDocument(); + + fireEvent.click(screen.getByTestId('checkboxSelectRow-1')); + + expect(screen.queryByTestId('totalRulesCount')).not.toBeInTheDocument(); + expect(screen.queryByTestId('showBulkActionButton')).toBeInTheDocument(); + expect(screen.queryByTestId('selectAllRulesButton')).toBeInTheDocument(); + }); + + it('selects all will select all items', async () => { + renderWithProviders(); + await waitForElementToBeRemoved(() => screen.queryByTestId('centerJustifiedSpinner')); + + fireEvent.click(screen.getByTestId('checkboxSelectRow-1')); + fireEvent.click(screen.getByTestId('selectAllRulesButton')); + + mockedRulesData.forEach((rule) => { + expect(screen.getByTestId(`checkboxSelectRow-${rule.id}`).closest('tr')).toHaveClass( + 'euiTableRow-isSelected' + ); + }); + + fireEvent.click(screen.getByTestId('showBulkActionButton')); + + expect(screen.queryByTestId('ruleQuickEditButton')).toBeInTheDocument(); + expect(screen.queryByTestId('bulkDisable')).toBeInTheDocument(); + expect(screen.queryByTestId('bulkEnable')).toBeInTheDocument(); + expect(screen.queryByTestId('bulkDelete')).toBeInTheDocument(); + }); + + it('does not render select all button if the user is not authorized', async () => { + loadRuleTypes.mockResolvedValue([ruleTypeFromApi, getDisabledByLicenseRuleTypeFromApi(false)]); + renderWithProviders(); + await waitForElementToBeRemoved(() => screen.queryByTestId('centerJustifiedSpinner')); + + fireEvent.click(screen.getByTestId('checkboxSelectRow-1')); + + expect(screen.queryByTestId('showBulkActionButton')).toBeInTheDocument(); + expect(screen.queryByTestId('selectAllRulesButton')).not.toBeInTheDocument(); + }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_bulk_enable.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_bulk_enable.test.tsx index 0cc662a28a69e..9ca4d2b5a277d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_bulk_enable.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_bulk_enable.test.tsx @@ -5,9 +5,17 @@ * 2.0. */ import * as React from 'react'; -import { ReactWrapper } from 'enzyme'; -import { act } from '@testing-library/react'; -import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; +import { + act, + render, + screen, + cleanup, + waitForElementToBeRemoved, + fireEvent, +} from '@testing-library/react'; +import { IToasts } from '@kbn/core/public'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { actionTypeRegistryMock } from '../../../action_type_registry.mock'; import { ruleTypeRegistryMock } from '../../../rule_type_registry.mock'; import { RulesList } from './rules_list'; @@ -19,7 +27,6 @@ import { getDisabledByLicenseRuleTypeFromApi, ruleType, } from './test_helpers'; -import { IToasts } from '@kbn/core/public'; jest.mock('../../../../common/lib/kibana'); jest.mock('@kbn/kibana-react-plugin/public/ui_settings/use_ui_setting', () => ({ @@ -30,7 +37,6 @@ jest.mock('../../../lib/action_connector_api', () => ({ loadActionTypes: jest.fn(), loadAllActions: jest.fn(), })); - jest.mock('../../../lib/rule_api/rules_kuery_filter', () => ({ loadRulesWithKueryFilter: jest.fn(), })); @@ -55,10 +61,8 @@ jest.mock('../../../lib/rule_api/health', () => ({ hasPermanentEncryptionKey: true, })), })); - jest.mock('../../../lib/rule_api/aggregate_kuery_filter'); jest.mock('../../../lib/rule_api/rules_kuery_filter'); - jest.mock('../../../../common/lib/health_api', () => ({ triggersActionsUiHealth: jest.fn(() => ({ isRulesAvailable: true })), })); @@ -75,7 +79,6 @@ jest.mock('react-router-dom', () => ({ pathname: '/triggersActions/rules/', }), })); - jest.mock('../../../lib/capabilities', () => ({ hasAllPrivilege: jest.fn(() => true), hasSaveRulesCapability: jest.fn(() => true), @@ -85,10 +88,15 @@ jest.mock('../../../lib/capabilities', () => ({ jest.mock('../../../../common/get_experimental_features', () => ({ getIsExperimentalFeatureEnabled: jest.fn(), })); +jest.mock('../../../lib/rule_api/aggregate_kuery_filter', () => ({ + loadRuleAggregationsWithKueryFilter: jest.fn(), +})); +const { loadRuleAggregationsWithKueryFilter } = jest.requireMock( + '../../../lib/rule_api/aggregate_kuery_filter' +); const { loadRuleTypes } = jest.requireMock('../../../lib/rule_api/rule_types'); const { bulkEnableRules } = jest.requireMock('../../../lib/rule_api/bulk_enable'); - const { loadRulesWithKueryFilter } = jest.requireMock('../../../lib/rule_api/rules_kuery_filter'); const { loadActionTypes, loadAllActions } = jest.requireMock('../../../lib/action_connector_api'); @@ -99,58 +107,40 @@ ruleTypeRegistry.list.mockReturnValue([ruleType]); actionTypeRegistry.list.mockReturnValue([]); const useKibanaMock = useKibana as jest.Mocked; - -beforeEach(() => { - (getIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(() => false); +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: false, + cacheTime: 0, + }, + }, }); -// Test are too slow. It's breaking the build. So we skipp it now and waiting for improvment according this ticket: -// https://github.com/elastic/kibana/issues/145122 -describe.skip('Rules list bulk enable', () => { - let wrapper: ReactWrapper; +const AllTheProviders = ({ children }: { children: any }) => ( + + {children} + +); - const setup = async (authorized: boolean = true) => { +const renderWithProviders = (ui: any) => { + return render(ui, { wrapper: AllTheProviders }); +}; + +describe('Rules list Bulk Enable', () => { + beforeAll(async () => { + (getIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(() => false); loadRulesWithKueryFilter.mockResolvedValue({ page: 1, perPage: 10000, total: 6, - data: mockedRulesData.map((rule) => ({ ...rule, enabled: false })), + data: mockedRulesData, }); - - loadActionTypes.mockResolvedValue([ - { - id: 'test', - name: 'Test', - }, - { - id: 'test2', - name: 'Test2', - }, - ]); - loadRuleTypes.mockResolvedValue([ - ruleTypeFromApi, - getDisabledByLicenseRuleTypeFromApi(authorized), - ]); + loadActionTypes.mockResolvedValue([]); + loadRuleTypes.mockResolvedValue([ruleTypeFromApi, getDisabledByLicenseRuleTypeFromApi()]); loadAllActions.mockResolvedValue([]); - // eslint-disable-next-line react-hooks/rules-of-hooks + loadRuleAggregationsWithKueryFilter.mockResolvedValue({}); useKibanaMock().services.ruleTypeRegistry = ruleTypeRegistry; - - // eslint-disable-next-line react-hooks/rules-of-hooks useKibanaMock().services.actionTypeRegistry = actionTypeRegistry; - wrapper = mountWithIntl(); - - await act(async () => { - await nextTick(); - wrapper.update(); - }); - }; - - afterEach(() => { - jest.clearAllMocks(); - }); - - beforeAll(async () => { - await setup(); useKibanaMock().services.notifications.toasts = { addSuccess: jest.fn(), addError: jest.fn(), @@ -159,22 +149,26 @@ describe.skip('Rules list bulk enable', () => { } as unknown as IToasts; }); - beforeEach(() => { - wrapper.find('[data-test-subj="checkboxSelectRow-1"]').at(1).simulate('change'); - wrapper.find('[data-test-subj="selectAllRulesButton"]').at(1).simulate('click'); - // Unselect something to test filtering - wrapper.find('[data-test-subj="checkboxSelectRow-2"]').at(1).simulate('change'); - wrapper.find('[data-test-subj="showBulkActionButton"]').first().simulate('click'); + afterEach(() => { + jest.clearAllMocks(); + queryClient.clear(); + cleanup(); + }); + + beforeEach(async () => { + renderWithProviders(); + await waitForElementToBeRemoved(() => screen.queryByTestId('centerJustifiedSpinner')); + + fireEvent.click(screen.getByTestId('checkboxSelectRow-1')); + fireEvent.click(screen.getByTestId('selectAllRulesButton')); + fireEvent.click(screen.getByTestId('checkboxSelectRow-2')); + fireEvent.click(screen.getByTestId('showBulkActionButton')); }); it('can bulk enable', async () => { - wrapper.find('button[data-test-subj="bulkEnable"]').first().simulate('click'); - await act(async () => { - await nextTick(); - wrapper.update(); + fireEvent.click(screen.getByTestId('bulkEnable')); }); - const filter = bulkEnableRules.mock.calls[0][0].filter; expect(filter.function).toEqual('and'); @@ -188,19 +182,16 @@ describe.skip('Rules list bulk enable', () => { ids: [], }) ); - expect( - wrapper.find('[data-test-subj="checkboxSelectRow-1"]').first().prop('checked') - ).toBeFalsy(); - expect(wrapper.find('button[data-test-subj="bulkEnable"]').exists()).toBeFalsy(); + expect(screen.getByTestId('checkboxSelectRow-1').closest('tr')).not.toHaveClass( + 'euiTableRow-isSelected' + ); + expect(screen.queryByTestId('bulkEnable')).not.toBeInTheDocument(); }); describe('Toast', () => { it('should have success toast message', async () => { - wrapper.find('button[data-test-subj="bulkEnable"]').first().simulate('click'); - await act(async () => { - await nextTick(); - wrapper.update(); + fireEvent.click(screen.getByTestId('bulkEnable')); }); expect(useKibanaMock().services.notifications.toasts.addSuccess).toHaveBeenCalledTimes(1); @@ -223,11 +214,8 @@ describe.skip('Rules list bulk enable', () => { total: 10, }); - wrapper.find('button[data-test-subj="bulkEnable"]').first().simulate('click'); - await act(async () => { - await nextTick(); - wrapper.update(); + fireEvent.click(screen.getByTestId('bulkEnable')); }); expect(useKibanaMock().services.notifications.toasts.addWarning).toHaveBeenCalledTimes(1); @@ -252,11 +240,8 @@ describe.skip('Rules list bulk enable', () => { total: 1, }); - wrapper.find('button[data-test-subj="bulkEnable"]').first().simulate('click'); - await act(async () => { - await nextTick(); - wrapper.update(); + fireEvent.click(screen.getByTestId('bulkEnable')); }); expect(useKibanaMock().services.notifications.toasts.addDanger).toHaveBeenCalledTimes(1); diff --git a/x-pack/plugins/upgrade_assistant/common/constants.ts b/x-pack/plugins/upgrade_assistant/common/constants.ts index 8097ddd7f97d6..4ed5cb9d072c8 100644 --- a/x-pack/plugins/upgrade_assistant/common/constants.ts +++ b/x-pack/plugins/upgrade_assistant/common/constants.ts @@ -7,9 +7,6 @@ export const API_BASE_PATH = '/api/upgrade_assistant'; -// Telemetry constants -export const UPGRADE_ASSISTANT_TELEMETRY = 'upgrade-assistant-telemetry'; - /** * This is the repository where Cloud stores its backup snapshots. */ diff --git a/x-pack/plugins/upgrade_assistant/server/plugin.ts b/x-pack/plugins/upgrade_assistant/server/plugin.ts index 05964122b9873..922fdc8d89465 100644 --- a/x-pack/plugins/upgrade_assistant/server/plugin.ts +++ b/x-pack/plugins/upgrade_assistant/server/plugin.ts @@ -29,11 +29,7 @@ import { registerUpgradeAssistantUsageCollector } from './lib/telemetry'; import { versionService } from './lib/version'; import { createReindexWorker } from './routes/reindex_indices'; import { registerRoutes } from './routes/register_routes'; -import { - telemetrySavedObjectType, - reindexOperationSavedObjectType, - mlSavedObjectType, -} from './saved_object_types'; +import { reindexOperationSavedObjectType, mlSavedObjectType } from './saved_object_types'; import { handleEsError } from './shared_imports'; import { RouteDependencies } from './types'; import type { UpgradeAssistantConfig } from './config'; @@ -88,7 +84,6 @@ export class UpgradeAssistantServerPlugin implements Plugin { this.licensing = licensing; savedObjects.registerType(reindexOperationSavedObjectType); - savedObjects.registerType(telemetrySavedObjectType); savedObjects.registerType(mlSavedObjectType); features.registerElasticsearchFeature({ diff --git a/x-pack/plugins/upgrade_assistant/server/saved_object_types/index.ts b/x-pack/plugins/upgrade_assistant/server/saved_object_types/index.ts index e394cac5100f9..fa8c1ac679ad6 100644 --- a/x-pack/plugins/upgrade_assistant/server/saved_object_types/index.ts +++ b/x-pack/plugins/upgrade_assistant/server/saved_object_types/index.ts @@ -6,5 +6,4 @@ */ export { reindexOperationSavedObjectType } from './reindex_operation_saved_object_type'; -export { telemetrySavedObjectType } from './telemetry_saved_object_type'; export { mlSavedObjectType } from './ml_upgrade_operation_saved_object_type'; diff --git a/x-pack/plugins/upgrade_assistant/server/saved_object_types/migrations/telemetry_saved_object_migrations.test.ts b/x-pack/plugins/upgrade_assistant/server/saved_object_types/migrations/telemetry_saved_object_migrations.test.ts deleted file mode 100644 index e1250ee0ebfe0..0000000000000 --- a/x-pack/plugins/upgrade_assistant/server/saved_object_types/migrations/telemetry_saved_object_migrations.test.ts +++ /dev/null @@ -1,41 +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 { telemetrySavedObjectMigrations } from './telemetry_saved_object_migrations'; - -describe('Telemetry saved object migration', () => { - describe('7.16.0', () => { - test('removes ui_open and ui_reindex attributes while preserving other attributes', () => { - const doc = { - type: 'upgrade-assistant-telemetry', - id: 'upgrade-assistant-telemetry', - attributes: { - 'test.property': 5, - 'ui_open.cluster': 1, - 'ui_open.indices': 1, - 'ui_open.overview': 1, - 'ui_reindex.close': 1, - 'ui_reindex.open': 1, - 'ui_reindex.start': 1, - 'ui_reindex.stop': 1, - }, - references: [], - updated_at: '2021-09-29T21:17:17.410Z', - migrationVersion: {}, - }; - - expect(telemetrySavedObjectMigrations['7.16.0'](doc)).toStrictEqual({ - type: 'upgrade-assistant-telemetry', - id: 'upgrade-assistant-telemetry', - attributes: { 'test.property': 5 }, - references: [], - updated_at: '2021-09-29T21:17:17.410Z', - migrationVersion: {}, - }); - }); - }); -}); diff --git a/x-pack/plugins/upgrade_assistant/server/saved_object_types/migrations/telemetry_saved_object_migrations.ts b/x-pack/plugins/upgrade_assistant/server/saved_object_types/migrations/telemetry_saved_object_migrations.ts deleted file mode 100644 index 7d26065ce0b82..0000000000000 --- a/x-pack/plugins/upgrade_assistant/server/saved_object_types/migrations/telemetry_saved_object_migrations.ts +++ /dev/null @@ -1,40 +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 { get, omit, flow, some } from 'lodash'; -import type { SavedObjectMigrationFn } from '@kbn/core/server'; - -const v716RemoveUnusedTelemetry: SavedObjectMigrationFn = (doc) => { - // Dynamically defined in 6.7 (https://github.com/elastic/kibana/pull/28878) - // and then statically defined in 7.8 (https://github.com/elastic/kibana/pull/64332). - const attributesBlocklist = [ - 'ui_open.cluster', - 'ui_open.indices', - 'ui_open.overview', - 'ui_reindex.close', - 'ui_reindex.open', - 'ui_reindex.start', - 'ui_reindex.stop', - ]; - - const isDocEligible = some(attributesBlocklist, (attribute: string) => { - return get(doc, 'attributes', attribute); - }); - - if (isDocEligible) { - return { - ...doc, - attributes: omit(doc.attributes, attributesBlocklist), - }; - } - - return doc; -}; - -export const telemetrySavedObjectMigrations = { - '7.16.0': flow(v716RemoveUnusedTelemetry), -}; diff --git a/x-pack/plugins/upgrade_assistant/server/saved_object_types/telemetry_saved_object_type.ts b/x-pack/plugins/upgrade_assistant/server/saved_object_types/telemetry_saved_object_type.ts deleted file mode 100644 index a097783022a6b..0000000000000 --- a/x-pack/plugins/upgrade_assistant/server/saved_object_types/telemetry_saved_object_type.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { SavedObjectsType } from '@kbn/core/server'; - -import { UPGRADE_ASSISTANT_TELEMETRY } from '../../common/constants'; -import { telemetrySavedObjectMigrations } from './migrations'; - -export const telemetrySavedObjectType: SavedObjectsType = { - name: UPGRADE_ASSISTANT_TELEMETRY, - hidden: false, - namespaceType: 'agnostic', - mappings: { - properties: { - features: { - properties: { - deprecation_logging: { - properties: { - enabled: { - type: 'boolean', - null_value: true, - }, - }, - }, - }, - }, - }, - }, - migrations: telemetrySavedObjectMigrations, -}; diff --git a/x-pack/test/accessibility/apps/dashboard_controls.ts b/x-pack/test/accessibility/apps/dashboard_controls.ts index 8560b65b5fd34..b53a25d543680 100644 --- a/x-pack/test/accessibility/apps/dashboard_controls.ts +++ b/x-pack/test/accessibility/apps/dashboard_controls.ts @@ -11,6 +11,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const a11y = getService('a11y'); const testSubjects = getService('testSubjects'); const kibanaServer = getService('kibanaServer'); + const PageObjects = getPageObjects(['common', 'dashboard', 'home', 'dashboardControls']); const browser = getService('browser'); @@ -56,7 +57,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('Options control panel & dashboard with options control', async () => { - await testSubjects.click('field-picker-select-OriginCityName'); + await PageObjects.dashboardControls.controlsEditorSetfield('OriginCityName'); await a11y.testAppSnapshot(); await testSubjects.click('control-editor-save'); await a11y.testAppSnapshot(); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/event_log.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/event_log.ts index 01bb215a63d6d..2ee65b9edc3e7 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/event_log.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/event_log.ts @@ -7,6 +7,7 @@ import expect from '@kbn/expect'; import { IValidatedEvent, nanosToMillis } from '@kbn/event-log-plugin/server'; +import { RuleNotifyWhen } from '@kbn/alerting-plugin/common'; import { ES_TEST_INDEX_NAME, ESTestIndexTool } from '@kbn/alerting-api-integration-helpers'; import { Spaces } from '../../../scenarios'; import { @@ -664,6 +665,7 @@ export default function eventLogTests({ getService }: FtrProviderContext) { params: {}, }, ], + notify_when: RuleNotifyWhen.CHANGE, }) ); @@ -683,7 +685,7 @@ export default function eventLogTests({ getService }: FtrProviderContext) { // make sure the counts of the # of events per type are as expected ['execute-start', { gte: 6 }], ['execute', { gte: 6 }], - ['execute-action', { equal: 7 }], + ['execute-action', { equal: 1 }], ['new-instance', { equal: 1 }], ['active-instance', { gte: 6 }], ['recovered-instance', { equal: 1 }], @@ -748,6 +750,7 @@ export default function eventLogTests({ getService }: FtrProviderContext) { params: {}, }, ], + notify_when: RuleNotifyWhen.CHANGE, }) ); @@ -767,7 +770,7 @@ export default function eventLogTests({ getService }: FtrProviderContext) { // make sure the counts of the # of events per type are as expected ['execute-start', { gte: 6 }], ['execute', { gte: 6 }], - ['execute-action', { equal: 6 }], + ['execute-action', { equal: 2 }], ['new-instance', { equal: 2 }], ['active-instance', { gte: 6 }], ['recovered-instance', { equal: 2 }], @@ -784,6 +787,173 @@ export default function eventLogTests({ getService }: FtrProviderContext) { .map((event) => event?.kibana?.alert?.flapping); expect(flapping).to.eql([false, true, true, true, true, true, true, true]); }); + + it('should generate expected events for flapping alerts that are mainly active with notifyWhen not set to "on status change"', async () => { + await supertest + .post(`${getUrlPrefix(space.id)}/internal/alerting/rules/settings/_flapping`) + .set('kbn-xsrf', 'foo') + .auth('superuser', 'superuser') + .send({ + enabled: true, + look_back_window: 3, + status_change_threshold: 2, + }) + .expect(200); + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.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 instance = [true, false, true, true, true, true, true]; + const pattern = { + instance, + }; + + const response = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + rule_type_id: 'test.patternFiring', + schedule: { interval: '1s' }, + throttle: null, + params: { + pattern, + }, + actions: [ + { + id: createdAction.id, + group: 'default', + params: {}, + }, + ], + }) + ); + + expect(response.status).to.eql(200); + const alertId = response.body.id; + objectRemover.add(space.id, alertId, 'rule', 'alerting'); + + // get the events we're expecting + const events = await retry.try(async () => { + return await getEventLog({ + getService, + spaceId: space.id, + type: 'alert', + id: alertId, + provider: 'alerting', + actions: new Map([ + // make sure the counts of the # of events per type are as expected + ['execute-start', { gte: 6 }], + ['execute', { gte: 6 }], + ['execute-action', { equal: 6 }], + ['new-instance', { equal: 1 }], + ['active-instance', { gte: 6 }], + ['recovered-instance', { equal: 1 }], + ]), + }); + }); + + const flapping = events + .filter( + (event) => + event?.event?.action === 'active-instance' || + event?.event?.action === 'recovered-instance' + ) + .map((event) => event?.kibana?.alert?.flapping); + const result = [false, true, true, false, false, false, false]; + expect(flapping).to.eql(result); + }); + + it('should generate expected events for flapping alerts that are mainly recovered with notifyWhen not set to "on status change"', async () => { + await supertest + .post(`${getUrlPrefix(space.id)}/internal/alerting/rules/settings/_flapping`) + .set('kbn-xsrf', 'foo') + .auth('superuser', 'superuser') + .send({ + enabled: true, + look_back_window: 3, + status_change_threshold: 2, + }) + .expect(200); + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.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 instance = [true, false, true, false, false, false, true]; + const pattern = { + instance, + }; + + const response = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + rule_type_id: 'test.patternFiring', + schedule: { interval: '1s' }, + throttle: null, + params: { + pattern, + }, + actions: [ + { + id: createdAction.id, + group: 'default', + params: {}, + }, + ], + }) + ); + + expect(response.status).to.eql(200); + const alertId = response.body.id; + objectRemover.add(space.id, alertId, 'rule', 'alerting'); + + // get the events we're expecting + const events = await retry.try(async () => { + return await getEventLog({ + getService, + spaceId: space.id, + type: 'alert', + id: alertId, + provider: 'alerting', + actions: new Map([ + // make sure the counts of the # of events per type are as expected + ['execute-start', { gte: 5 }], + ['execute', { gte: 5 }], + ['execute-action', { equal: 3 }], + ['new-instance', { equal: 2 }], + ['active-instance', { gte: 3 }], + ['recovered-instance', { equal: 2 }], + ]), + }); + }); + + const flapping = events + .filter( + (event) => + event?.event?.action === 'active-instance' || + event?.event?.action === 'recovered-instance' + ) + .map((event) => event?.kibana?.alert?.flapping); + expect(flapping).to.eql([false, true, true, true, true]); + }); }); } }); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data.ts index 3ac2fdb93add8..ad2c33b079b0a 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data.ts @@ -5,20 +5,24 @@ * 2.0. */ -import { alertFieldMap } from '@kbn/alerting-plugin/common/alert_schema'; -import { mappingFromFieldMap } from '@kbn/alerting-plugin/common/alert_schema/field_maps/mapping_from_field_map'; +import { alertFieldMap, ecsFieldMap, legacyAlertFieldMap } from '@kbn/alerts-as-data-utils'; +import { mappingFromFieldMap } from '@kbn/alerting-plugin/common'; import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function createAlertsAsDataTest({ getService }: FtrProviderContext) { const es = getService('es'); - const commonFrameworkMappings = mappingFromFieldMap(alertFieldMap, 'strict'); + const frameworkMappings = mappingFromFieldMap(alertFieldMap, 'strict'); + const legacyAlertMappings = mappingFromFieldMap(legacyAlertFieldMap, 'strict'); + const ecsMappings = mappingFromFieldMap(ecsFieldMap, 'strict'); describe('alerts as data', () => { it('should install common alerts as data resources on startup', async () => { const ilmPolicyName = '.alerts-ilm-policy'; - const componentTemplateName = 'alerts-common-component-template'; + const frameworkComponentTemplateName = '.alerts-framework-mappings'; + const legacyComponentTemplateName = '.alerts-legacy-alert-mappings'; + const ecsComponentTemplateName = '.alerts-ecs-mappings'; const commonIlmPolicy = await es.ilm.getLifecycle({ name: ilmPolicyName, @@ -41,23 +45,65 @@ export default function createAlertsAsDataTest({ getService }: FtrProviderContex }, }); - const { component_templates: componentTemplates } = await es.cluster.getComponentTemplate({ - name: componentTemplateName, + const { component_templates: componentTemplates1 } = await es.cluster.getComponentTemplate({ + name: frameworkComponentTemplateName, }); - expect(componentTemplates.length).to.eql(1); - const commonComponentTemplate = componentTemplates[0]; + expect(componentTemplates1.length).to.eql(1); + const frameworkComponentTemplate = componentTemplates1[0]; + + expect(frameworkComponentTemplate.name).to.eql(frameworkComponentTemplateName); + expect(frameworkComponentTemplate.component_template.template.mappings).to.eql( + frameworkMappings + ); + expect(frameworkComponentTemplate.component_template.template.settings).to.eql({ + index: { + number_of_shards: 1, + mapping: { + total_fields: { + limit: 1500, + }, + }, + }, + }); + + const { component_templates: componentTemplates2 } = await es.cluster.getComponentTemplate({ + name: legacyComponentTemplateName, + }); + + expect(componentTemplates2.length).to.eql(1); + const legacyComponentTemplate = componentTemplates2[0]; - expect(commonComponentTemplate.name).to.eql(componentTemplateName); - expect(commonComponentTemplate.component_template.template.mappings).to.eql( - commonFrameworkMappings + expect(legacyComponentTemplate.name).to.eql(legacyComponentTemplateName); + expect(legacyComponentTemplate.component_template.template.mappings).to.eql( + legacyAlertMappings ); - expect(commonComponentTemplate.component_template.template.settings).to.eql({ + expect(legacyComponentTemplate.component_template.template.settings).to.eql({ + index: { + number_of_shards: 1, + mapping: { + total_fields: { + limit: 1500, + }, + }, + }, + }); + + const { component_templates: componentTemplates3 } = await es.cluster.getComponentTemplate({ + name: ecsComponentTemplateName, + }); + + expect(componentTemplates3.length).to.eql(1); + const ecsComponentTemplate = componentTemplates3[0]; + + expect(ecsComponentTemplate.name).to.eql(ecsComponentTemplateName); + expect(ecsComponentTemplate.component_template.template.mappings).to.eql(ecsMappings); + expect(ecsComponentTemplate.component_template.template.settings).to.eql({ index: { number_of_shards: 1, mapping: { total_fields: { - limit: 100, + limit: 2500, }, }, }, @@ -65,7 +111,7 @@ export default function createAlertsAsDataTest({ getService }: FtrProviderContex }); it('should install context specific alerts as data resources on startup', async () => { - const componentTemplateName = 'alerts-test.always-firing-component-template'; + const componentTemplateName = '.alerts-test.always-firing-mappings'; const indexTemplateName = '.alerts-test.always-firing-default-template'; const indexName = '.alerts-test.always-firing-default-000001'; const contextSpecificMappings = { @@ -98,7 +144,7 @@ export default function createAlertsAsDataTest({ getService }: FtrProviderContex number_of_shards: 1, mapping: { total_fields: { - limit: 100, + limit: 1500, }, }, }, @@ -114,8 +160,8 @@ export default function createAlertsAsDataTest({ getService }: FtrProviderContex '.alerts-test.always-firing-default-*', ]); expect(contextIndexTemplate.index_template.composed_of).to.eql([ - 'alerts-common-component-template', - 'alerts-test.always-firing-component-template', + '.alerts-test.always-firing-mappings', + '.alerts-framework-mappings', ]); expect(contextIndexTemplate.index_template.template!.mappings).to.eql({ dynamic: false, @@ -150,7 +196,7 @@ export default function createAlertsAsDataTest({ getService }: FtrProviderContex dynamic: 'false', properties: { ...contextSpecificMappings, - ...commonFrameworkMappings.properties, + ...frameworkMappings.properties, }, }); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/event_log_alerts.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/event_log_alerts.ts index a7f954bad78e2..97ef276fef930 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/event_log_alerts.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/event_log_alerts.ts @@ -58,7 +58,7 @@ export default function eventLogAlertTests({ getService }: FtrProviderContext) { // make sure the counts of the # of events per type are as expected ['execute', { gte: 12 }], ['new-instance', { equal: 2 }], - ['active-instance', { gte: 8 }], + ['active-instance', { gte: 5 }], ['recovered-instance', { equal: 2 }], ]), }); @@ -132,9 +132,7 @@ export default function eventLogAlertTests({ getService }: FtrProviderContext) { break; } } - expect(flapping).to.eql( - new Array(instanceEvents.length - 4).fill(false).concat([true, true, true, true]) - ); + expect(flapping).to.eql(new Array(instanceEvents.length - 1).fill(false).concat([true])); }); }); } diff --git a/x-pack/test/api_integration/apis/aiops/config.ts b/x-pack/test/api_integration/apis/aiops/config.ts index 5f335f116fefe..aba6dda02eecc 100644 --- a/x-pack/test/api_integration/apis/aiops/config.ts +++ b/x-pack/test/api_integration/apis/aiops/config.ts @@ -13,5 +13,8 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { return { ...baseIntegrationTestsConfig.getAll(), testFiles: [require.resolve('.')], + junit: { + reportName: 'X-Pack API Integration Tests - aiops', + }, }; } diff --git a/x-pack/test/api_integration/apis/logstash/pipelines/list.ts b/x-pack/test/api_integration/apis/logstash/pipelines/list.ts index 6833bd8131cd6..0734795e00cb0 100644 --- a/x-pack/test/api_integration/apis/logstash/pipelines/list.ts +++ b/x-pack/test/api_integration/apis/logstash/pipelines/list.ts @@ -12,7 +12,8 @@ import pipelineList from './fixtures/list.json'; export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); - describe('list', () => { + // Failing: See https://github.com/elastic/kibana/issues/151350 + describe.skip('list', () => { const archive = 'x-pack/test/functional/es_archives/logstash/example_pipelines'; before('load pipelines archive', () => { diff --git a/x-pack/test/api_integration/apis/ml/config.ts b/x-pack/test/api_integration/apis/ml/config.ts index 5f335f116fefe..925eda2a5bea8 100644 --- a/x-pack/test/api_integration/apis/ml/config.ts +++ b/x-pack/test/api_integration/apis/ml/config.ts @@ -13,5 +13,8 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { return { ...baseIntegrationTestsConfig.getAll(), testFiles: [require.resolve('.')], + junit: { + reportName: 'Chrome X-Pack UI Functional Tests - ml', + }, }; } diff --git a/x-pack/test/api_integration/apis/transform/config.ts b/x-pack/test/api_integration/apis/transform/config.ts index 5f335f116fefe..7dac20656b8ea 100644 --- a/x-pack/test/api_integration/apis/transform/config.ts +++ b/x-pack/test/api_integration/apis/transform/config.ts @@ -13,5 +13,8 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { return { ...baseIntegrationTestsConfig.getAll(), testFiles: [require.resolve('.')], + junit: { + reportName: 'Chrome X-Pack UI Functional Tests - transform', + }, }; } 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 5aadd7df90949..2914f6a17d509 100644 --- a/x-pack/test/cases_api_integration/common/lib/alerts.ts +++ b/x-pack/test/cases_api_integration/common/lib/alerts.ts @@ -10,7 +10,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { ToolingLog } from '@kbn/tooling-log'; import { DETECTION_ENGINE_QUERY_SIGNALS_URL } from '@kbn/security-solution-plugin/common/constants'; import { DetectionAlert } from '@kbn/security-solution-plugin/common/detection_engine/schemas/alerts'; -import { RiskEnrichmentFields } from '@kbn/security-solution-plugin/server/lib/detection_engine/signals/enrichments/types'; +import { RiskEnrichmentFields } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_types/utils/enrichments/types'; import { getRuleForSignalTesting, createRule, 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 1c9534c84d04b..c15c19232edf6 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 @@ -38,7 +38,7 @@ import { CasesMetricsResponse, CasesBulkGetResponse, } from '@kbn/cases-plugin/common/api'; -import { SignalHit } from '@kbn/security-solution-plugin/server/lib/detection_engine/signals/types'; +import { SignalHit } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_types/types'; import { ActionResult } from '@kbn/actions-plugin/server/types'; import { ESCasesConfigureAttributes } from '@kbn/cases-plugin/server/services/configure/types'; import { ESCaseAttributes } from '@kbn/cases-plugin/server/services/cases/types'; diff --git a/x-pack/test/defend_workflows_cypress/agent.ts b/x-pack/test/defend_workflows_cypress/agent.ts index c34eb728432c9..91bf42d8adcf6 100644 --- a/x-pack/test/defend_workflows_cypress/agent.ts +++ b/x-pack/test/defend_workflows_cypress/agent.ts @@ -21,6 +21,8 @@ export class AgentManager extends Manager { public async setup() { this.vmName = await enrollEndpointHost(); + + return this.vmName; } public cleanup() { diff --git a/x-pack/test/defend_workflows_cypress/runner.ts b/x-pack/test/defend_workflows_cypress/runner.ts index fddf7867ba28c..12831e785a56b 100644 --- a/x-pack/test/defend_workflows_cypress/runner.ts +++ b/x-pack/test/defend_workflows_cypress/runner.ts @@ -14,9 +14,11 @@ import { AgentManager } from './agent'; import { FleetManager } from './fleet_server'; import { getLatestAvailableAgentVersion } from './utils'; +type RunnerEnv = Record; + async function withFleetAgent( { getService }: FtrProviderContext, - runner: (runnerEnv: Record) => Promise + runner: (runnerEnv: RunnerEnv) => Promise ) { const log = getService('log'); const config = getService('config'); @@ -40,9 +42,9 @@ async function withFleetAgent( const agentManager = new AgentManager(log); await fleetManager.setup(); - await agentManager.setup(); + const agentVmName = await agentManager.setup(); try { - await runner({}); + await runner({ agentVmName }); } finally { agentManager.cleanup(); fleetManager.cleanup(); @@ -58,10 +60,16 @@ export async function DefendWorkflowsCypressVisualTestRunner(context: FtrProvide } export async function DefendWorkflowsCypressEndpointTestRunner(context: FtrProviderContext) { - await withFleetAgent(context, () => startDefendWorkflowsCypress(context, 'dw:endpoint:open')); + await withFleetAgent(context, (runnerEnv) => + startDefendWorkflowsCypress(context, 'dw:endpoint:open', runnerEnv) + ); } -function startDefendWorkflowsCypress(context: FtrProviderContext, cypressCommand: string) { +function startDefendWorkflowsCypress( + context: FtrProviderContext, + cypressCommand: 'dw:endpoint:open' | 'dw:open' | 'dw:run', + runnerEnv?: RunnerEnv +) { const log = context.getService('log'); const config = context.getService('config'); return withProcRunner(log, async (procs) => { @@ -91,6 +99,7 @@ function startDefendWorkflowsCypress(context: FtrProviderContext, cypressCommand hostname: config.get('servers.kibana.hostname'), port: config.get('servers.kibana.port'), }), + CYPRESS_ENDPOINT_VM_NAME: runnerEnv?.agentVmName, ...process.env, }, wait: true, diff --git a/x-pack/test/defend_workflows_cypress/utils.ts b/x-pack/test/defend_workflows_cypress/utils.ts index 2f0b8baa1e856..2f13105f9b364 100644 --- a/x-pack/test/defend_workflows_cypress/utils.ts +++ b/x-pack/test/defend_workflows_cypress/utils.ts @@ -7,7 +7,7 @@ import axios from 'axios'; import semver from 'semver'; -import { filter } from 'lodash'; +import { map } from 'lodash'; import { KbnClient } from '@kbn/test'; /** @@ -20,9 +20,7 @@ export const getLatestAvailableAgentVersion = async (kbnClient: KbnClient): Prom const kbnStatus = await kbnClient.status.get(); const agentVersions = await axios .get('https://artifacts-api.elastic.co/v1/versions') - .then((response) => - filter(response.data.versions, (versionString) => !versionString.includes('SNAPSHOT')) - ); + .then((response) => map(response.data.versions, (version) => version.split('-SNAPSHOT')[0])); let version = semver.maxSatisfying(agentVersions, `<=${kbnStatus.version.number}`) ?? diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/preview_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/preview_rules.ts index 5c301030d5abe..b930f43dc9809 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/preview_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/preview_rules.ts @@ -69,6 +69,19 @@ export default ({ getService }: FtrProviderContext) => { ]); expect(body).to.eql({ logs }); }); + + it('should limit concurrent requests to 10', async () => { + const responses = await Promise.all( + Array.from({ length: 15 }).map(() => + supertest + .post(DETECTION_ENGINE_RULES_PREVIEW) + .set('kbn-xsrf', 'true') + .send(getSimplePreviewRule()) + ) + ); + + expect(responses.filter((r) => r.body.statusCode === 429).length).to.eql(5); + }); }); describe('t1_analyst', () => { diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/create_signals_migrations.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/create_signals_migrations.ts index 5a5011f05aac5..4445e63b54aaf 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/create_signals_migrations.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/create_signals_migrations.ts @@ -14,7 +14,7 @@ import { } from '@kbn/security-solution-plugin/common/constants'; import { ROLES } from '@kbn/security-solution-plugin/common/test'; import { SIGNALS_TEMPLATE_VERSION } from '@kbn/security-solution-plugin/server/lib/detection_engine/routes/index/get_signals_template'; -import { Signal } from '@kbn/security-solution-plugin/server/lib/detection_engine/signals/types'; +import { Signal } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_types/types'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createSignalsIndex, diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/import_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/import_rules.ts index 41b49ac35bfc6..123e7bdbed2e5 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/import_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/import_rules.ts @@ -93,6 +93,84 @@ const getImportRuleBuffer = (connectorId: string) => { const buffer = Buffer.from(`${rule1String}\n`); return buffer; }; +const getImportRuleWithConnectorsBuffer = (connectorId: string) => { + const rule1 = { + id: '53aad690-544e-11ec-a349-11361cc441c4', + updated_at: '2021-12-03T15:33:13.271Z', + updated_by: 'elastic', + created_at: '2021-12-03T15:33:13.271Z', + created_by: 'elastic', + name: '7.16 test with action', + tags: [], + interval: '5m', + enabled: true, + description: 'test', + risk_score: 21, + severity: 'low', + license: '', + output_index: '', + meta: { from: '1m', kibana_siem_app_url: 'http://0.0.0.0:5601/s/7/app/security' }, + author: [], + false_positives: [], + from: 'now-360s', + rule_id: 'aa525d7c-8948-439f-b32d-27e00c750246', + max_signals: 100, + risk_score_mapping: [], + severity_mapping: [], + threat: [], + to: 'now', + references: [], + version: 1, + exceptions_list: [], + immutable: false, + type: 'query', + language: 'kuery', + index: [ + 'apm-*-transaction*', + 'traces-apm*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + ], + query: '*:*', + filters: [], + throttle: '1h', + actions: [ + { + group: 'default', + id: connectorId, + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + }, + action_type_id: '.slack', + }, + ], + }; + const connector = { + id: connectorId, + type: 'action', + updated_at: '2023-01-25T14:35:52.852Z', + created_at: '2023-01-25T14:35:52.852Z', + version: 'WzUxNTksMV0=', + attributes: { + actionTypeId: '.slack', + name: 'slack', + isMissingSecrets: false, + config: {}, + secrets: {}, + }, + references: [], + migrationVersion: { action: '8.3.0' }, + coreMigrationVersion: '8.7.0', + }; + const rule1String = JSON.stringify(rule1); + const connectorString = JSON.stringify(connector); + const buffer = Buffer.from(`${rule1String}\n${connectorString}`); + return buffer; +}; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { @@ -1115,95 +1193,179 @@ export default ({ getService }: FtrProviderContext): void => { ); }); - it('importing a non-default-space 7.16 rule with a connector made in the non-default space should result in a 200', async () => { - const spaceId = '714-space'; - // connectorId is from the 7.x connector here - // x-pack/test/functional/es_archives/security_solution/import_rule_connector - const buffer = getImportRuleBuffer(space714ActionConnectorId); - - const { body } = await supertest - .post(`/s/${spaceId}${DETECTION_ENGINE_RULES_URL}/_import`) - .set('kbn-xsrf', 'true') - .attach('file', buffer, 'rules.ndjson') - .expect(200); - expect(body.success).to.eql(true); - expect(body.success_count).to.eql(1); - expect(body.errors.length).to.eql(0); - }); - - // When objects become share-capable we will either add / update this test - it('importing a non-default-space 7.16 rule with a connector made in the non-default space into the default space should result in a 404', async () => { - // connectorId is from the 7.x connector here - // x-pack/test/functional/es_archives/security_solution/import_rule_connector - const buffer = getImportRuleBuffer(space714ActionConnectorId); - - const { body } = await supertest - .post(`${DETECTION_ENGINE_RULES_URL}/_import`) - .set('kbn-xsrf', 'true') - .attach('file', buffer, 'rules.ndjson') - .expect(200); - expect(body.success).to.equal(false); - expect(body.errors[0].error.status_code).to.equal(404); - expect(body.errors[0].error.message).to.equal( - `1 connector is missing. Connector id missing is: ${space714ActionConnectorId}` - ); - }); - - // When objects become share-capable we will either add / update this test - it('importing a non-default-space 7.16 rule with a connector made in the non-default space into a different non-default space should result in a 404', async () => { - const spaceId = '4567-space'; - // connectorId is from the 7.x connector here - // x-pack/test/functional/es_archives/security_solution/import_rule_connector - // it - const buffer = getImportRuleBuffer(space714ActionConnectorId); - - const { body } = await supertest - .post(`/s/${spaceId}${DETECTION_ENGINE_RULES_URL}/_import`) - .set('kbn-xsrf', 'true') - .attach('file', buffer, 'rules.ndjson') - .expect(200); - expect(body.success).to.equal(false); - expect(body.errors[0].error.status_code).to.equal(404); - expect(body.errors[0].error.message).to.equal( - `1 connector is missing. Connector id missing is: ${space714ActionConnectorId}` - ); - }); - - it('importing a default-space 7.16 rule with a connector made in the default space into the default space should result in a 200', async () => { - // connectorId is from the 7.x connector here - // x-pack/test/functional/es_archives/security_solution/import_rule_connector - // it - const buffer = getImportRuleBuffer(defaultSpaceActionConnectorId); + describe('should be imported into the non-default space', () => { + it('importing a non-default-space 7.16 rule with a connector made in the non-default space should result in a 200', async () => { + const spaceId = '714-space'; + // connectorId is from the 7.x connector here + // x-pack/test/functional/es_archives/security_solution/import_rule_connector + const buffer = getImportRuleBuffer(space714ActionConnectorId); + + const { body } = await supertest + .post(`/s/${spaceId}${DETECTION_ENGINE_RULES_URL}/_import`) + .set('kbn-xsrf', 'true') + .attach('file', buffer, 'rules.ndjson') + .expect(200); + expect(body.success).to.eql(true); + expect(body.success_count).to.eql(1); + expect(body.errors.length).to.eql(0); + }); - const { body } = await supertest - .post(`${DETECTION_ENGINE_RULES_URL}/_import`) - .set('kbn-xsrf', 'true') - .attach('file', buffer, 'rules.ndjson') - .expect(200); - expect(body.success).to.equal(true); - expect(body.success_count).to.eql(1); - expect(body.errors.length).to.eql(0); + it('should import a non-default-space 7.16 rule with a connector made in the non-default space', async () => { + const spaceId = '714-space'; + const differentSpaceConnectorId = '5272d090-b111-11ed-b56a-a7991a8d8b32'; + + const buffer = getImportRuleWithConnectorsBuffer(differentSpaceConnectorId); + const { body } = await supertest + .post(`/s/${spaceId}${DETECTION_ENGINE_RULES_URL}/_import`) + .set('kbn-xsrf', 'true') + .attach('file', buffer, 'rules.ndjson') + .expect(200); + + expect(body).to.eql({ + success: true, + success_count: 1, + rules_count: 1, + errors: [], + exceptions_errors: [], + exceptions_success: true, + exceptions_success_count: 0, + action_connectors_success: true, + action_connectors_success_count: 1, + action_connectors_warnings: [], + action_connectors_errors: [], + }); + }); + it('should import a non-default-space 7.16 rule with a connector made in the non-default space into the default space successfully', async () => { + // connectorId is from the 7.x connector here + // x-pack/test/functional/es_archives/security_solution/import_rule_connector + const differentSpaceConnectorId = '963ec960-a21a-11ed-84a4-a33e4c2558c9'; + const buffer = getImportRuleWithConnectorsBuffer(differentSpaceConnectorId); + + const { body } = await supertest + .post(`${DETECTION_ENGINE_RULES_URL}/_import`) + .set('kbn-xsrf', 'true') + .attach('file', buffer, 'rules.ndjson') + .expect(200); + expect(body).to.eql({ + success: true, + success_count: 1, + rules_count: 1, + errors: [], + exceptions_errors: [], + exceptions_success: true, + exceptions_success_count: 0, + action_connectors_success: true, + action_connectors_success_count: 1, + action_connectors_warnings: [], + action_connectors_errors: [], + }); + }); + it('importing a non-default-space 7.16 rule with a connector made in the non-default space into the default space should result in a 404 if the file does not contain connectors', async () => { + // connectorId is from the 7.x connector here + // x-pack/test/functional/es_archives/security_solution/import_rule_connector + const buffer = getImportRuleBuffer(space714ActionConnectorId); + + const { body } = await supertest + .post(`${DETECTION_ENGINE_RULES_URL}/_import`) + .set('kbn-xsrf', 'true') + .attach('file', buffer, 'rules.ndjson') + .expect(200); + expect(body.success).to.equal(false); + expect(body.errors[0].error.status_code).to.equal(404); + expect(body.errors[0].error.message).to.equal( + `1 connector is missing. Connector id missing is: ${space714ActionConnectorId}` + ); + }); + // When objects become share-capable we will either add / update this test + it('importing a non-default-space 7.16 rule with a connector made in the non-default space into a different non-default space should result in a 404', async () => { + const spaceId = '4567-space'; + // connectorId is from the 7.x connector here + // x-pack/test/functional/es_archives/security_solution/import_rule_connector + // it + const buffer = getImportRuleBuffer(space714ActionConnectorId); + + const { body } = await supertest + .post(`/s/${spaceId}${DETECTION_ENGINE_RULES_URL}/_import`) + .set('kbn-xsrf', 'true') + .attach('file', buffer, 'rules.ndjson') + .expect(200); + expect(body.success).to.equal(false); + expect(body.errors[0].error.status_code).to.equal(404); + expect(body.errors[0].error.message).to.equal( + `1 connector is missing. Connector id missing is: ${space714ActionConnectorId}` + ); + }); }); - it('importing a default-space 7.16 rule with a connector made in the default space into a non-default space should result in a 404', async () => { - await esArchiver.load( - 'x-pack/test/functional/es_archives/security_solution/import_rule_connector' - ); - const spaceId = '4567-space'; - // connectorId is from the 7.x connector here - // x-pack/test/functional/es_archives/security_solution/import_rule_connector - // it - const buffer = getImportRuleBuffer(defaultSpaceActionConnectorId); - - const { body } = await supertest - .post(`/s/${spaceId}${DETECTION_ENGINE_RULES_URL}/_import`) - .set('kbn-xsrf', 'true') - .attach('file', buffer, 'rules.ndjson') - .expect(200); - expect(body.success).to.equal(false); - expect(body.errors[0].error.status_code).to.equal(404); - expect(body.errors[0].error.message).to.equal( - `1 connector is missing. Connector id missing is: ${defaultSpaceActionConnectorId}` - ); + describe('should be imported into the default space', () => { + it('should import a default-space 7.16 rule with a connector made in the default space into a non-default space successfully', async () => { + await esArchiver.load( + 'x-pack/test/functional/es_archives/security_solution/import_rule_connector' + ); + const defaultSpaceConnectorId = '8fbf6d10-a21a-11ed-84a4-a33e4c2558c9'; + + const spaceId = '4567-space'; + // connectorId is from the 7.x connector here + // x-pack/test/functional/es_archives/security_solution/import_rule_connector + // it + const buffer = getImportRuleWithConnectorsBuffer(defaultSpaceConnectorId); + + const { body } = await supertest + .post(`/s/${spaceId}${DETECTION_ENGINE_RULES_URL}/_import`) + .set('kbn-xsrf', 'true') + .attach('file', buffer, 'rules.ndjson') + .expect(200); + expect(body).to.eql({ + success: true, + success_count: 1, + rules_count: 1, + errors: [], + exceptions_errors: [], + exceptions_success: true, + exceptions_success_count: 0, + action_connectors_success: true, + action_connectors_success_count: 1, + action_connectors_warnings: [], + action_connectors_errors: [], + }); + }); + // When objects become share-capable we will either add / update this test + + it('importing a default-space 7.16 rule with a connector made in the default space into the default space should result in a 200', async () => { + // connectorId is from the 7.x connector here + // x-pack/test/functional/es_archives/security_solution/import_rule_connector + // it + const buffer = getImportRuleBuffer(defaultSpaceActionConnectorId); + + const { body } = await supertest + .post(`${DETECTION_ENGINE_RULES_URL}/_import`) + .set('kbn-xsrf', 'true') + .attach('file', buffer, 'rules.ndjson') + .expect(200); + expect(body.success).to.equal(true); + expect(body.success_count).to.eql(1); + expect(body.errors.length).to.eql(0); + }); + it('importing a default-space 7.16 rule with a connector made in the default space into a non-default space should result in a 404', async () => { + await esArchiver.load( + 'x-pack/test/functional/es_archives/security_solution/import_rule_connector' + ); + const spaceId = '4567-space'; + // connectorId is from the 7.x connector here + // x-pack/test/functional/es_archives/security_solution/import_rule_connector + // it + const buffer = getImportRuleBuffer(defaultSpaceActionConnectorId); + + const { body } = await supertest + .post(`/s/${spaceId}${DETECTION_ENGINE_RULES_URL}/_import`) + .set('kbn-xsrf', 'true') + .attach('file', buffer, 'rules.ndjson') + .expect(200); + expect(body.success).to.equal(false); + expect(body.errors[0].error.status_code).to.equal(404); + expect(body.errors[0].error.message).to.equal( + `1 connector is missing. Connector id missing is: ${defaultSpaceActionConnectorId}` + ); + }); }); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/eql.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/eql.ts index 64d71539eb065..ced077145f5a9 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/eql.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/eql.ts @@ -17,7 +17,7 @@ import { flattenWithPrefix } from '@kbn/securitysolution-rules'; import { get } from 'lodash'; import { EqlRuleCreateProps } from '@kbn/security-solution-plugin/common/detection_engine/rule_schema'; -import { Ancestor } from '@kbn/security-solution-plugin/server/lib/detection_engine/signals/types'; +import { Ancestor } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_types/types'; import { ALERT_ANCESTORS, ALERT_DEPTH, diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/machine_learning.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/machine_learning.ts index fe12454cf4591..099104ad411f7 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/machine_learning.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/machine_learning.ts @@ -64,7 +64,8 @@ export default ({ getService }: FtrProviderContext) => { rule_id: 'ml-rule-id', }; - describe('Machine learning type rules', () => { + // FLAKY: https://github.com/elastic/kibana/issues/145776 + describe.skip('Machine learning type rules', () => { before(async () => { // Order is critical here: auditbeat data must be loaded before attempting to start the ML job, // as the job looks for certain indices on start diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/query.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/query.ts index 446163e0fc05b..c77ac2049fde5 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/query.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/query.ts @@ -26,7 +26,7 @@ import { v4 as uuidv4 } from 'uuid'; import { QueryRuleCreateProps } from '@kbn/security-solution-plugin/common/detection_engine/rule_schema'; import { RuleExecutionStatus } from '@kbn/security-solution-plugin/common/detection_engine/rule_monitoring'; -import { Ancestor } from '@kbn/security-solution-plugin/server/lib/detection_engine/signals/types'; +import { Ancestor } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_types/types'; import { ALERT_ANCESTORS, ALERT_DEPTH, diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/threat_match.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/threat_match.ts index df9a4fbce88ff..0f92db72d3c2a 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/threat_match.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/threat_match.ts @@ -23,7 +23,7 @@ import { flattenWithPrefix } from '@kbn/securitysolution-rules'; import { ThreatMatchRuleCreateProps } from '@kbn/security-solution-plugin/common/detection_engine/rule_schema'; import { ENRICHMENT_TYPES } from '@kbn/security-solution-plugin/common/cti/constants'; -import { Ancestor } from '@kbn/security-solution-plugin/server/lib/detection_engine/signals/types'; +import { Ancestor } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_types/types'; import { ALERT_ANCESTORS, ALERT_DEPTH, diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/threshold.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/threshold.ts index 45b24902e8bc5..3dc878c4e2ba0 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/threshold.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/threshold.ts @@ -14,7 +14,7 @@ import { } from '@kbn/rule-data-utils'; import { ThresholdRuleCreateProps } from '@kbn/security-solution-plugin/common/detection_engine/rule_schema'; -import { Ancestor } from '@kbn/security-solution-plugin/server/lib/detection_engine/signals/types'; +import { Ancestor } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_types/types'; import { ALERT_ANCESTORS, ALERT_DEPTH, diff --git a/x-pack/test/detection_engine_api_integration/utils/get_preview_alerts.ts b/x-pack/test/detection_engine_api_integration/utils/get_preview_alerts.ts index 2d9cdc0137546..716f46dfcb815 100644 --- a/x-pack/test/detection_engine_api_integration/utils/get_preview_alerts.ts +++ b/x-pack/test/detection_engine_api_integration/utils/get_preview_alerts.ts @@ -8,7 +8,7 @@ import type { Client } from '@elastic/elasticsearch'; import { ALERT_RULE_UUID } from '@kbn/rule-data-utils'; import { DetectionAlert } from '@kbn/security-solution-plugin/common/detection_engine/schemas/alerts'; -import { RiskEnrichmentFields } from '@kbn/security-solution-plugin/server/lib/detection_engine/signals/enrichments/types'; +import { RiskEnrichmentFields } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_types/utils/enrichments/types'; import { refreshIndex } from './refresh_index'; /** diff --git a/x-pack/test/detection_engine_api_integration/utils/get_signals_by_ids.ts b/x-pack/test/detection_engine_api_integration/utils/get_signals_by_ids.ts index faea391b18602..d5740d91934eb 100644 --- a/x-pack/test/detection_engine_api_integration/utils/get_signals_by_ids.ts +++ b/x-pack/test/detection_engine_api_integration/utils/get_signals_by_ids.ts @@ -9,7 +9,7 @@ import { SearchResponse } from '@elastic/elasticsearch/lib/api/typesWithBodyKey' import type { ToolingLog } from '@kbn/tooling-log'; import type SuperTest from 'supertest'; import type { DetectionAlert } from '@kbn/security-solution-plugin/common/detection_engine/schemas/alerts'; -import type { RiskEnrichmentFields } from '@kbn/security-solution-plugin/server/lib/detection_engine/signals/enrichments/types'; +import type { RiskEnrichmentFields } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_types/utils/enrichments/types'; import { DETECTION_ENGINE_QUERY_SIGNALS_URL } from '@kbn/security-solution-plugin/common/constants'; import { countDownTest } from './count_down_test'; diff --git a/x-pack/test/fleet_api_integration/apis/agents/reassign.ts b/x-pack/test/fleet_api_integration/apis/agents/reassign.ts index 2dd546511dd9a..571a0fee11f54 100644 --- a/x-pack/test/fleet_api_integration/apis/agents/reassign.ts +++ b/x-pack/test/fleet_api_integration/apis/agents/reassign.ts @@ -37,7 +37,7 @@ export default function (providerContext: FtrProviderContext) { describe('reassign single agent', () => { it('should allow to reassign single agent', async () => { await supertest - .put(`/api/fleet/agents/agent1/reassign`) + .post(`/api/fleet/agents/agent1/reassign`) .set('kbn-xsrf', 'xxx') .send({ policy_id: 'policy2', @@ -49,7 +49,7 @@ export default function (providerContext: FtrProviderContext) { it('should throw an error for invalid policy id for single reassign', async () => { await supertest - .put(`/api/fleet/agents/agent1/reassign`) + .post(`/api/fleet/agents/agent1/reassign`) .set('kbn-xsrf', 'xxx') .send({ policy_id: 'INVALID_ID', @@ -61,7 +61,7 @@ export default function (providerContext: FtrProviderContext) { // policy2 is not hosted // reassign succeeds await supertest - .put(`/api/fleet/agents/agent1/reassign`) + .post(`/api/fleet/agents/agent1/reassign`) .set('kbn-xsrf', 'xxx') .send({ policy_id: 'policy2', @@ -79,7 +79,7 @@ export default function (providerContext: FtrProviderContext) { // reassign fails await supertest - .put(`/api/fleet/agents/agent1/reassign`) + .post(`/api/fleet/agents/agent1/reassign`) .set('kbn-xsrf', 'xxx') .send({ policy_id: 'policy2', diff --git a/x-pack/test/fleet_api_integration/apis/epm/index.js b/x-pack/test/fleet_api_integration/apis/epm/index.js index 48af135f15ae2..867ab6f6fe3e4 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/index.js +++ b/x-pack/test/fleet_api_integration/apis/epm/index.js @@ -32,5 +32,6 @@ export default function loadTests({ loadTestFile }) { loadTestFile(require.resolve('./install_error_rollback')); loadTestFile(require.resolve('./final_pipeline')); loadTestFile(require.resolve('./custom_ingest_pipeline')); + loadTestFile(require.resolve('./verification_key_id')); }); } diff --git a/x-pack/test/fleet_api_integration/apis/epm/verification_key_id.ts b/x-pack/test/fleet_api_integration/apis/epm/verification_key_id.ts new file mode 100644 index 0000000000000..4c5468f35606e --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/epm/verification_key_id.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + describe('EPM - verification key id', async () => { + it('returns the verification key ID ', async () => { + const res = await supertest + .get('/api/fleet/epm/verification_key_id') + .set('kbn-xsrf', 'xxx') + .expect(200); + + expect(res.body.id).equal('d2a182a7b0e00c14'); + }); + }); +} diff --git a/x-pack/test/osquery_cypress/agent.ts b/x-pack/test/osquery_cypress/agent.ts index 2bdecd90efdff..e35b2f1bc4d37 100644 --- a/x-pack/test/osquery_cypress/agent.ts +++ b/x-pack/test/osquery_cypress/agent.ts @@ -7,7 +7,7 @@ import { ToolingLog } from '@kbn/tooling-log'; import axios, { AxiosRequestConfig } from 'axios'; -import { ChildProcess, spawn } from 'child_process'; +import execa from 'execa'; import { getLatestVersion } from './artifact_manager'; import { Manager } from './resource_manager'; @@ -22,7 +22,7 @@ export interface AgentManagerParams { export class AgentManager extends Manager { private params: AgentManagerParams; private log: ToolingLog; - private agentProcess?: ChildProcess; + private agentContainerId?: string; private requestOptions: AxiosRequestConfig; constructor(params: AgentManagerParams, log: ToolingLog, requestOptions: AxiosRequestConfig) { super(); @@ -33,34 +33,40 @@ export class AgentManager extends Manager { public async setup() { this.log.info('Running agent preconfig'); - return await axios.post( - `${this.params.kibanaUrl}/api/fleet/agents/setup`, - {}, + + await axios.post( + `${this.params.kibanaUrl}/api/fleet/agent_policies?sys_monitoring=true`, + { + name: 'Osquery policy', + description: '', + namespace: 'default', + monitoring_enabled: ['logs', 'metrics'], + inactivity_timeout: 1209600, + }, this.requestOptions ); - } - public async startAgent() { this.log.info('Getting agent enrollment key'); const { data: apiKeys } = await axios.get( this.params.kibanaUrl + '/api/fleet/enrollment_api_keys', this.requestOptions ); - const policy = apiKeys.items[1]; + const policy = apiKeys.items[0]; this.log.info('Running the agent'); const artifact = `docker.elastic.co/beats/elastic-agent:${await getLatestVersion()}`; this.log.info(artifact); - const args = [ + const dockerArgs = [ 'run', + '--detach', '--add-host', 'host.docker.internal:host-gateway', '--env', 'FLEET_ENROLL=1', '--env', - `FLEET_URL=http://host.docker.internal:8220`, + `FLEET_URL=https://host.docker.internal:8220`, '--env', `FLEET_ENROLLMENT_TOKEN=${policy.api_key}`, '--env', @@ -69,7 +75,7 @@ export class AgentManager extends Manager { artifact, ]; - this.agentProcess = spawn('docker', args, { stdio: 'inherit' }); + this.agentContainerId = (await execa('docker', dockerArgs)).stdout; // Wait til we see the agent is online let done = false; @@ -92,15 +98,11 @@ export class AgentManager extends Manager { protected _cleanup() { this.log.info('Cleaning up the agent process'); - if (this.agentProcess) { - if (!this.agentProcess.kill(9)) { - this.log.warning('Unable to kill agent process'); - } + if (this.agentContainerId) { + this.log.info('Closing agent process'); - this.agentProcess.on('close', () => { - this.log.info('Agent process closed'); - }); - delete this.agentProcess; + execa.sync('docker', ['kill', this.agentContainerId]); + this.log.info('Agent process closed'); } return; } diff --git a/x-pack/test/osquery_cypress/artifact_manager.ts b/x-pack/test/osquery_cypress/artifact_manager.ts index 7ee2680e21f83..2a520c22203c0 100644 --- a/x-pack/test/osquery_cypress/artifact_manager.ts +++ b/x-pack/test/osquery_cypress/artifact_manager.ts @@ -6,5 +6,5 @@ */ export async function getLatestVersion(): Promise { - return '8.6.0-SNAPSHOT'; + return '8.8.0-SNAPSHOT'; } diff --git a/x-pack/test/osquery_cypress/config.ts b/x-pack/test/osquery_cypress/config.ts index 37f7b3f63b36c..76b5c3eca9f89 100644 --- a/x-pack/test/osquery_cypress/config.ts +++ b/x-pack/test/osquery_cypress/config.ts @@ -35,9 +35,11 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { ...xpackFunctionalTestsConfig.get('kbnTestServer'), serverArgs: [ ...xpackFunctionalTestsConfig.get('kbnTestServer.serverArgs'), + '--csp.warnLegacyBrowsers=false', '--csp.strict=false', // define custom kibana server args here `--elasticsearch.ssl.certificateAuthorities=${CA_CERT_PATH}`, + `--xpack.fleet.agents.fleet_server.hosts=["https://host.docker.internal:8220"]`, `--xpack.fleet.agents.elasticsearch.host=http://host.docker.internal:${kibanaCommonTestsConfig.get( 'servers.elasticsearch.port' )}`, diff --git a/x-pack/test/osquery_cypress/fleet_server.ts b/x-pack/test/osquery_cypress/fleet_server.ts index 77ec56cf20960..ce1ff24adc32a 100644 --- a/x-pack/test/osquery_cypress/fleet_server.ts +++ b/x-pack/test/osquery_cypress/fleet_server.ts @@ -5,15 +5,15 @@ * 2.0. */ -import { ChildProcess, spawn } from 'child_process'; import { ToolingLog } from '@kbn/tooling-log'; import axios, { AxiosRequestConfig } from 'axios'; +import execa from 'execa'; import { Manager } from './resource_manager'; import { getLatestVersion } from './artifact_manager'; import { AgentManagerParams } from './agent'; export class FleetManager extends Manager { - private fleetProcess?: ChildProcess; + private fleetContainerId?: string; private config: AgentManagerParams; private log: ToolingLog; private requestOptions: AxiosRequestConfig; @@ -25,77 +25,65 @@ export class FleetManager extends Manager { } public async setup(): Promise { this.log.info('Setting fleet up'); - return new Promise(async (res, rej) => { - try { - // default fleet server policy no longer created by default - const { - data: { - item: { id: policyId }, - }, - } = await axios.post( - `${this.config.kibanaUrl}/api/fleet/agent_policies`, - { - name: 'Default Fleet Server policy', - description: '', - namespace: 'default', - monitoring_enabled: ['logs', 'metrics'], - has_fleet_server: true, - }, - this.requestOptions - ); - const response = await axios.post( - `${this.config.kibanaUrl}/api/fleet/service_tokens`, - {}, - this.requestOptions - ); - const serviceToken = response.data.value; - const artifact = `docker.elastic.co/beats/elastic-agent:${await getLatestVersion()}`; - this.log.info(artifact); + // default fleet server policy no longer created by default + const { + data: { + item: { id: policyId }, + }, + } = await axios.post( + `${this.config.kibanaUrl}/api/fleet/agent_policies`, + { + name: 'Default Fleet Server policy', + description: '', + namespace: 'default', + monitoring_enabled: ['logs', 'metrics'], + has_fleet_server: true, + }, + this.requestOptions + ); - const host = 'host.docker.internal'; + const response = await axios.post( + `${this.config.kibanaUrl}/api/fleet/service_tokens`, + {}, + this.requestOptions + ); + const serviceToken = response.data.value; + const artifact = `docker.elastic.co/beats/elastic-agent:${await getLatestVersion()}`; + this.log.info(artifact); - const args = [ - 'run', - '-p', - `8220:8220`, - '--add-host', - 'host.docker.internal:host-gateway', - '--env', - 'FLEET_SERVER_ENABLE=true', - '--env', - `FLEET_SERVER_ELASTICSEARCH_HOST=http://${host}:${this.config.esPort}`, - '--env', - `FLEET_SERVER_SERVICE_TOKEN=${serviceToken}`, - '--env', - `FLEET_SERVER_POLICY=${policyId}`, - '--rm', - artifact, - ]; - this.log.info('docker ' + args.join(' ')); - this.fleetProcess = spawn('docker', args, { - stdio: 'inherit', - }); - this.fleetProcess.on('error', rej); - setTimeout(res, 15000); - } catch (error) { - rej(error); - } - }); + const host = 'host.docker.internal'; + + const dockerArgs = [ + 'run', + '--detach', + '-p', + `8220:8220`, + '--add-host', + 'host.docker.internal:host-gateway', + '--env', + 'FLEET_SERVER_ENABLE=true', + '--env', + `FLEET_SERVER_ELASTICSEARCH_HOST=http://${host}:${this.config.esPort}`, + '--env', + `FLEET_SERVER_SERVICE_TOKEN=${serviceToken}`, + '--env', + `FLEET_SERVER_POLICY=${policyId}`, + '--rm', + artifact, + ]; + + this.log.info('docker ' + dockerArgs.join(' ')); + this.fleetContainerId = (await execa('docker', dockerArgs)).stdout; } protected _cleanup() { this.log.info('Removing old fleet config'); - if (this.fleetProcess) { + if (this.fleetContainerId) { this.log.info('Closing fleet process'); - if (!this.fleetProcess.kill(9)) { - this.log.warning('Unable to kill fleet server process'); - } - this.fleetProcess.on('close', () => { - this.log.info('Fleet server process closed'); - }); - delete this.fleetProcess; + execa.sync('docker', ['kill', this.fleetContainerId]); + this.log.info('Fleet server process closed'); } } } diff --git a/x-pack/test/osquery_cypress/runner.ts b/x-pack/test/osquery_cypress/runner.ts index 2c97d08b682c3..2cd194c14ea53 100644 --- a/x-pack/test/osquery_cypress/runner.ts +++ b/x-pack/test/osquery_cypress/runner.ts @@ -12,10 +12,7 @@ import { withProcRunner } from '@kbn/dev-proc-runner'; import { FtrProviderContext } from './ftr_provider_context'; -import { - // AgentManager, - AgentManagerParams, -} from './agent'; +import { AgentManager, AgentManagerParams } from './agent'; import { FleetManager } from './fleet_server'; async function withFleetAgent( @@ -47,8 +44,7 @@ async function withFleetAgent( }, }; const fleetManager = new FleetManager(params, log, requestOptions); - - // const agentManager = new AgentManager(params, log, requestOptions); + const agentManager = new AgentManager(params, log, requestOptions); // Since the managers will create uncaughtException event handlers we need to exit manually process.on('uncaughtException', (err) => { @@ -57,13 +53,13 @@ async function withFleetAgent( process.exit(1); }); - // await agentManager.setup(); await fleetManager.setup(); + await agentManager.setup(); try { await runner({}); } finally { + agentManager.cleanup(); fleetManager.cleanup(); - // agentManager.cleanup(); } } diff --git a/x-pack/test/rule_registry/spaces_only/tests/trial/get_summarized_alerts.ts b/x-pack/test/rule_registry/spaces_only/tests/trial/get_summarized_alerts.ts index 02765aa8c2a77..b62cf7b39965c 100644 --- a/x-pack/test/rule_registry/spaces_only/tests/trial/get_summarized_alerts.ts +++ b/x-pack/test/rule_registry/spaces_only/tests/trial/get_summarized_alerts.ts @@ -10,7 +10,7 @@ import type { ElasticsearchClient, Logger, LogMeta } from '@kbn/core/server'; import sinon from 'sinon'; import { v4 as uuidv4 } from 'uuid'; import expect from '@kbn/expect'; -import { mappingFromFieldMap } from '@kbn/rule-registry-plugin/common/mapping_from_field_map'; +import { mappingFromFieldMap } from '@kbn/alerting-plugin/common'; import { AlertConsumers, ALERT_REASON, diff --git a/x-pack/test/rule_registry/spaces_only/tests/trial/lifecycle_executor.ts b/x-pack/test/rule_registry/spaces_only/tests/trial/lifecycle_executor.ts index dc5752417bfb4..10351fc6cf2ef 100644 --- a/x-pack/test/rule_registry/spaces_only/tests/trial/lifecycle_executor.ts +++ b/x-pack/test/rule_registry/spaces_only/tests/trial/lifecycle_executor.ts @@ -9,7 +9,7 @@ import { type Subject, ReplaySubject } from 'rxjs'; import type { ElasticsearchClient, Logger, LogMeta } from '@kbn/core/server'; import sinon from 'sinon'; import expect from '@kbn/expect'; -import { mappingFromFieldMap } from '@kbn/rule-registry-plugin/common/mapping_from_field_map'; +import { mappingFromFieldMap } from '@kbn/alerting-plugin/common'; import { AlertConsumers, ALERT_REASON, diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts index 6aab2457c5278..589a61295a541 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts @@ -15,7 +15,8 @@ import { export default function (providerContext: FtrProviderContext) { const { loadTestFile, getService } = providerContext; - describe('endpoint', function () { + // FLAKY: https://github.com/elastic/kibana/issues/72874 + describe.skip('endpoint', function () { const ingestManager = getService('ingestManager'); const log = getService('log'); const endpointTestResources = getService('endpointTestResources'); diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_artifacts/event_filters.ts b/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_artifacts/event_filters.ts index de6f0d73f2c7c..47adc6526236c 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_artifacts/event_filters.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_artifacts/event_filters.ts @@ -184,7 +184,7 @@ export default function ({ getService }: FtrProviderContext) { body.entries[0].field = 'some.invalid.field'; await supertestWithoutAuth[eventFilterApiCall.method](eventFilterApiCall.path) - .auth(ROLE.analyst_hunter, 'changeme') + .auth(ROLE.endpoint_security_policy_manager, 'changeme') .set('kbn-xsrf', 'true') .send(body) .expect(400) @@ -196,7 +196,7 @@ export default function ({ getService }: FtrProviderContext) { const body = eventFilterApiCall.getBody({ os_types: ['linux', 'windows'] }); await supertestWithoutAuth[eventFilterApiCall.method](eventFilterApiCall.path) - .auth(ROLE.analyst_hunter, 'changeme') + .auth(ROLE.endpoint_security_policy_manager, 'changeme') .set('kbn-xsrf', 'true') .send(body) .expect(400) @@ -211,7 +211,7 @@ export default function ({ getService }: FtrProviderContext) { // Using superuser there as we need custom license for this action await supertest[eventFilterApiCall.method](eventFilterApiCall.path) - .auth(ROLE.analyst_hunter, 'changeme') + .auth(ROLE.endpoint_security_policy_manager, 'changeme') .set('kbn-xsrf', 'true') .send(body) .expect(400) @@ -224,7 +224,7 @@ export default function ({ getService }: FtrProviderContext) { // Using superuser here as we need custom license for this action await supertest[eventFilterApiCall.method](eventFilterApiCall.path) - .auth(ROLE.analyst_hunter, 'changeme') + .auth(ROLE.endpoint_security_policy_manager, 'changeme') .set('kbn-xsrf', 'true') .send(body) .expect(200); @@ -236,7 +236,7 @@ export default function ({ getService }: FtrProviderContext) { for (const eventFilterApiCall of [...needsWritePrivilege, ...needsReadPrivilege]) { it(`should not error on [${eventFilterApiCall.method}] - [${eventFilterApiCall.info}]`, async () => { await supertestWithoutAuth[eventFilterApiCall.method](eventFilterApiCall.path) - .auth(ROLE.analyst_hunter, 'changeme') + .auth(ROLE.endpoint_security_policy_manager, 'changeme') .set('kbn-xsrf', 'true') .send(eventFilterApiCall.getBody()) .expect(200); diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_artifacts/host_isolation_exceptions.ts b/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_artifacts/host_isolation_exceptions.ts index 765d34da5d603..b22ff51ce8ed7 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_artifacts/host_isolation_exceptions.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_artifacts/host_isolation_exceptions.ts @@ -191,7 +191,7 @@ export default function ({ getService }: FtrProviderContext) { await supertestWithoutAuth[hostIsolationExceptionApiCall.method]( hostIsolationExceptionApiCall.path ) - .auth(ROLE.analyst_hunter, 'changeme') + .auth(ROLE.endpoint_security_policy_manager, 'changeme') .set('kbn-xsrf', 'true') .send(body) .expect(400) @@ -207,7 +207,7 @@ export default function ({ getService }: FtrProviderContext) { await supertestWithoutAuth[hostIsolationExceptionApiCall.method]( hostIsolationExceptionApiCall.path ) - .auth(ROLE.analyst_hunter, 'changeme') + .auth(ROLE.endpoint_security_policy_manager, 'changeme') .set('kbn-xsrf', 'true') .send(body) .expect(400) @@ -230,7 +230,7 @@ export default function ({ getService }: FtrProviderContext) { await supertestWithoutAuth[hostIsolationExceptionApiCall.method]( hostIsolationExceptionApiCall.path ) - .auth(ROLE.analyst_hunter, 'changeme') + .auth(ROLE.endpoint_security_policy_manager, 'changeme') .set('kbn-xsrf', 'true') .send(body) .expect(400) @@ -246,7 +246,7 @@ export default function ({ getService }: FtrProviderContext) { await supertestWithoutAuth[hostIsolationExceptionApiCall.method]( hostIsolationExceptionApiCall.path ) - .auth(ROLE.analyst_hunter, 'changeme') + .auth(ROLE.endpoint_security_policy_manager, 'changeme') .set('kbn-xsrf', 'true') .send(body) .expect(400) @@ -273,7 +273,7 @@ export default function ({ getService }: FtrProviderContext) { await supertestWithoutAuth[hostIsolationExceptionApiCall.method]( hostIsolationExceptionApiCall.path ) - .auth(ROLE.analyst_hunter, 'changeme') + .auth(ROLE.endpoint_security_policy_manager, 'changeme') .set('kbn-xsrf', 'true') .send(hostIsolationExceptionApiCall.getBody()) .expect(200); diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_artifacts/trusted_apps.ts b/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_artifacts/trusted_apps.ts index 4f26fabe97bff..9841cf2adab9c 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_artifacts/trusted_apps.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_artifacts/trusted_apps.ts @@ -155,7 +155,7 @@ export default function ({ getService }: FtrProviderContext) { body.entries[0].field = 'some.invalid.field'; await supertestWithoutAuth[trustedAppApiCall.method](trustedAppApiCall.path) - .auth(ROLE.analyst_hunter, 'changeme') + .auth(ROLE.endpoint_security_policy_manager, 'changeme') .set('kbn-xsrf', 'true') .send(body) .expect(400) @@ -169,7 +169,7 @@ export default function ({ getService }: FtrProviderContext) { body.entries.push({ ...body.entries[0] }); await supertestWithoutAuth[trustedAppApiCall.method](trustedAppApiCall.path) - .auth(ROLE.analyst_hunter, 'changeme') + .auth(ROLE.endpoint_security_policy_manager, 'changeme') .set('kbn-xsrf', 'true') .send(body) .expect(400) @@ -190,7 +190,7 @@ export default function ({ getService }: FtrProviderContext) { ]; await supertestWithoutAuth[trustedAppApiCall.method](trustedAppApiCall.path) - .auth(ROLE.analyst_hunter, 'changeme') + .auth(ROLE.endpoint_security_policy_manager, 'changeme') .set('kbn-xsrf', 'true') .send(body) .expect(400) @@ -224,7 +224,7 @@ export default function ({ getService }: FtrProviderContext) { ]; await supertestWithoutAuth[trustedAppApiCall.method](trustedAppApiCall.path) - .auth(ROLE.analyst_hunter, 'changeme') + .auth(ROLE.endpoint_security_policy_manager, 'changeme') .set('kbn-xsrf', 'true') .send(body) .expect(400) @@ -238,7 +238,7 @@ export default function ({ getService }: FtrProviderContext) { body.os_types = ['linux', 'windows']; await supertestWithoutAuth[trustedAppApiCall.method](trustedAppApiCall.path) - .auth(ROLE.analyst_hunter, 'changeme') + .auth(ROLE.endpoint_security_policy_manager, 'changeme') .set('kbn-xsrf', 'true') .send(body) .expect(400) @@ -253,7 +253,7 @@ export default function ({ getService }: FtrProviderContext) { // Using superuser here as we need custom license for this action await supertest[trustedAppApiCall.method](trustedAppApiCall.path) - .auth(ROLE.analyst_hunter, 'changeme') + .auth(ROLE.endpoint_security_policy_manager, 'changeme') .set('kbn-xsrf', 'true') .send(body) .expect(400) @@ -264,7 +264,7 @@ export default function ({ getService }: FtrProviderContext) { for (const trustedAppApiCall of [...needsWritePrivilege, ...needsReadPrivilege]) { it(`should not error on [${trustedAppApiCall.method}] - [${trustedAppApiCall.info}]`, async () => { await supertestWithoutAuth[trustedAppApiCall.method](trustedAppApiCall.path) - .auth(ROLE.analyst_hunter, 'changeme') + .auth(ROLE.endpoint_security_policy_manager, 'changeme') .set('kbn-xsrf', 'true') .send(trustedAppApiCall.getBody()) .expect(200); diff --git a/x-pack/test/security_solution_endpoint_api_int/services/roles_users.ts b/x-pack/test/security_solution_endpoint_api_int/services/roles_users.ts index 930d0c1ddcba6..baac39815b488 100644 --- a/x-pack/test/security_solution_endpoint_api_int/services/roles_users.ts +++ b/x-pack/test/security_solution_endpoint_api_int/services/roles_users.ts @@ -10,7 +10,7 @@ import type { Role } from '@kbn/security-plugin/common'; import { getT1Analyst } from '@kbn/security-solution-plugin/scripts/endpoint/common/roles_users/t1_analyst'; import { getT2Analyst } from '@kbn/security-solution-plugin/scripts/endpoint/common/roles_users/t2_analyst'; import { getHunter } from '@kbn/security-solution-plugin/scripts/endpoint/common/roles_users/hunter'; -import { getThreadIntelligenceAnalyst } from '@kbn/security-solution-plugin/scripts/endpoint/common/roles_users/thread_intelligence_analyst'; +import { getThreatIntelligenceAnalyst } from '@kbn/security-solution-plugin/scripts/endpoint/common/roles_users/threat_intelligence_analyst'; import { getDetectionsEngineer } from '@kbn/security-solution-plugin/scripts/endpoint/common/roles_users/detections_engineer'; import { getSocManager } from '@kbn/security-solution-plugin/scripts/endpoint/common/roles_users/soc_manager'; import { getPlatformEngineer } from '@kbn/security-solution-plugin/scripts/endpoint/common/roles_users/platform_engineer'; @@ -25,7 +25,7 @@ export enum ROLE { t1_analyst = 't1Analyst', t2_analyst = 't2Analyst', analyst_hunter = 'hunter', - thread_intelligence_analyst = 'threadIntelligenceAnalyst', + threat_intelligence_analyst = 'threatIntelligenceAnalyst', detections_engineer = 'detectionsEngineer', soc_manager = 'socManager', platform_engineer = 'platformEngineer', @@ -39,7 +39,7 @@ const rolesMapping: { [key in ROLE]: Omit } = { t1Analyst: getT1Analyst(), t2Analyst: getT2Analyst(), hunter: getHunter(), - threadIntelligenceAnalyst: getThreadIntelligenceAnalyst(), + threatIntelligenceAnalyst: getThreatIntelligenceAnalyst(), detectionsEngineer: getDetectionsEngineer(), socManager: getSocManager(), platformEngineer: getPlatformEngineer(), diff --git a/x-pack/test/tsconfig.json b/x-pack/test/tsconfig.json index 476b6223b9591..be736ec73fd0d 100644 --- a/x-pack/test/tsconfig.json +++ b/x-pack/test/tsconfig.json @@ -115,6 +115,7 @@ "@kbn/cloud-security-posture-plugin", "@kbn/cloud-integration-saml-provider-plugin", "@kbn/security-api-integration-helpers", + "@kbn/alerts-as-data-utils", "@kbn/discover-plugin", ] } diff --git a/yarn.lock b/yarn.lock index 2d8f0c0b35eff..6ffc80f4e34d5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,12 +7,13 @@ resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.0.1.tgz#b38b444ad3aa5fedbb15f2f746dcd934226a12dd" integrity sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g== -"@ampproject/remapping@^2.1.0": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.1.2.tgz#4edca94973ded9630d20101cd8559cedb8d8bd34" - integrity sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg== +"@ampproject/remapping@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" + integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== dependencies: - "@jridgewell/trace-mapping" "^0.3.0" + "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/trace-mapping" "^0.3.9" "@apidevtools/json-schema-ref-parser@^9.0.6": version "9.0.9" @@ -56,12 +57,12 @@ resolved "https://registry.yarnpkg.com/@assemblyscript/loader/-/loader-0.10.1.tgz#70e45678f06c72fa2e350e8553ec4a4d72b92e06" integrity sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg== -"@babel/cli@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.20.7.tgz#8fc12e85c744a1a617680eacb488fab1fcd35b7c" - integrity sha512-WylgcELHB66WwQqItxNILsMlaTd8/SO6SgTTjMp4uCI7P4QyH1r3nqgFmO3BfM4AtfniHgFMH3EpYFj/zynBkQ== +"@babel/cli@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.21.0.tgz#1868eb70e9824b427fc607610cce8e9e7889e7e1" + integrity sha512-xi7CxyS8XjSyiwUGCfwf+brtJxjW1/ZTcBUkP10xawIEXLX5HzLn+3aXkgxozcP2UhRhtKTmQurw9Uaes7jZrA== dependencies: - "@jridgewell/trace-mapping" "^0.3.8" + "@jridgewell/trace-mapping" "^0.3.17" commander "^4.0.1" convert-source-map "^1.1.0" fs-readdir-recursive "^1.1.0" @@ -113,21 +114,21 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/core@^7.1.0", "@babel/core@^7.11.6", "@babel/core@^7.12.10", "@babel/core@^7.12.3", "@babel/core@^7.20.12", "@babel/core@^7.7.2", "@babel/core@^7.7.5": - version "7.20.12" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.12.tgz#7930db57443c6714ad216953d1356dac0eb8496d" - integrity sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg== +"@babel/core@^7.1.0", "@babel/core@^7.11.6", "@babel/core@^7.12.10", "@babel/core@^7.12.3", "@babel/core@^7.21.0", "@babel/core@^7.7.2", "@babel/core@^7.7.5": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.0.tgz#1341aefdcc14ccc7553fcc688dd8986a2daffc13" + integrity sha512-PuxUbxcW6ZYe656yL3EAhpy7qXKq0DmYsrJLpbB8XrsCP9Nm+XCg9XFMb5vIDliPD7+U/+M+QJlH17XOcB7eXA== dependencies: - "@ampproject/remapping" "^2.1.0" + "@ampproject/remapping" "^2.2.0" "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.7" + "@babel/generator" "^7.21.0" "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-module-transforms" "^7.20.11" - "@babel/helpers" "^7.20.7" - "@babel/parser" "^7.20.7" + "@babel/helper-module-transforms" "^7.21.0" + "@babel/helpers" "^7.21.0" + "@babel/parser" "^7.21.0" "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.12" - "@babel/types" "^7.20.7" + "@babel/traverse" "^7.21.0" + "@babel/types" "^7.21.0" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" @@ -150,13 +151,14 @@ dependencies: eslint-rule-composer "^0.3.0" -"@babel/generator@^7.12.11", "@babel/generator@^7.12.5", "@babel/generator@^7.20.14", "@babel/generator@^7.20.7", "@babel/generator@^7.7.2": - version "7.20.14" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.14.tgz#9fa772c9f86a46c6ac9b321039400712b96f64ce" - integrity sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg== +"@babel/generator@^7.12.11", "@babel/generator@^7.12.5", "@babel/generator@^7.21.0", "@babel/generator@^7.21.1", "@babel/generator@^7.7.2": + version "7.21.1" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.1.tgz#951cc626057bc0af2c35cd23e9c64d384dea83dd" + integrity sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA== dependencies: - "@babel/types" "^7.20.7" + "@babel/types" "^7.21.0" "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" "@babel/helper-annotate-as-pure@^7.16.0", "@babel/helper-annotate-as-pure@^7.18.6": @@ -185,17 +187,18 @@ lru-cache "^5.1.1" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.19.0", "@babel/helper-create-class-features-plugin@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.2.tgz#3c08a5b5417c7f07b5cf3dfb6dc79cbec682e8c2" - integrity sha512-k22GoYRAHPYr9I+Gvy2ZQlAe5mGy8BqWst2wRt8cwIufWTxrsVshhIBvYNqC80N0GSFWTsqRVexOtfzlgOEDvA== +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.20.2", "@babel/helper-create-class-features-plugin@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.0.tgz#64f49ecb0020532f19b1d014b03bccaa1ab85fb9" + integrity sha512-Q8wNiMIdwsv5la5SPxNYzzkPnjgC0Sy0i7jLkVOCdllu/xcVNkr3TeZzbHBJrj+XXRqzX5uCyCoV9eu6xUG7KQ== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-member-expression-to-functions" "^7.18.9" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-member-expression-to-functions" "^7.21.0" "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-replace-supers" "^7.19.1" + "@babel/helper-replace-supers" "^7.20.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/helper-split-export-declaration" "^7.18.6" "@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.19.0": @@ -244,13 +247,13 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" - integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== +"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0", "@babel/helper-function-name@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" + integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== dependencies: - "@babel/template" "^7.18.10" - "@babel/types" "^7.19.0" + "@babel/template" "^7.20.7" + "@babel/types" "^7.21.0" "@babel/helper-hoist-variables@^7.18.6": version "7.18.6" @@ -259,12 +262,12 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-member-expression-to-functions@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz#1531661e8375af843ad37ac692c132841e2fd815" - integrity sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg== +"@babel/helper-member-expression-to-functions@^7.20.7", "@babel/helper-member-expression-to-functions@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz#319c6a940431a133897148515877d2f3269c3ba5" + integrity sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q== dependencies: - "@babel/types" "^7.18.9" + "@babel/types" "^7.21.0" "@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.0", "@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.18.6": version "7.18.6" @@ -273,10 +276,10 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.19.6", "@babel/helper-module-transforms@^7.20.11": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz#df4c7af713c557938c50ea3ad0117a7944b2f1b0" - integrity sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg== +"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.19.6", "@babel/helper-module-transforms@^7.21.0": + version "7.21.2" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz#160caafa4978ac8c00ac66636cb0fa37b024e2d2" + integrity sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ== dependencies: "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-module-imports" "^7.18.6" @@ -284,8 +287,8 @@ "@babel/helper-split-export-declaration" "^7.18.6" "@babel/helper-validator-identifier" "^7.19.1" "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.10" - "@babel/types" "^7.20.7" + "@babel/traverse" "^7.21.2" + "@babel/types" "^7.21.2" "@babel/helper-optimise-call-expression@^7.18.6": version "7.18.6" @@ -314,16 +317,17 @@ "@babel/helper-wrap-function" "^7.18.9" "@babel/types" "^7.18.9" -"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.19.1.tgz#e1592a9b4b368aa6bdb8784a711e0bcbf0612b78" - integrity sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw== +"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.19.1", "@babel/helper-replace-supers@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz#243ecd2724d2071532b2c8ad2f0f9f083bcae331" + integrity sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A== dependencies: "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-member-expression-to-functions" "^7.18.9" + "@babel/helper-member-expression-to-functions" "^7.20.7" "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/traverse" "^7.19.1" - "@babel/types" "^7.19.0" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.20.7" + "@babel/types" "^7.20.7" "@babel/helper-simple-access@^7.19.4", "@babel/helper-simple-access@^7.20.2": version "7.20.2" @@ -356,10 +360,10 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== -"@babel/helper-validator-option@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" - integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== +"@babel/helper-validator-option@^7.18.6", "@babel/helper-validator-option@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" + integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== "@babel/helper-wrap-function@^7.18.9": version "7.19.0" @@ -371,14 +375,14 @@ "@babel/traverse" "^7.19.0" "@babel/types" "^7.19.0" -"@babel/helpers@^7.12.5", "@babel/helpers@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.7.tgz#04502ff0feecc9f20ecfaad120a18f011a8e6dce" - integrity sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA== +"@babel/helpers@^7.12.5", "@babel/helpers@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.21.0.tgz#9dd184fb5599862037917cdc9eecb84577dc4e7e" + integrity sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA== dependencies: "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.7" - "@babel/types" "^7.20.7" + "@babel/traverse" "^7.21.0" + "@babel/types" "^7.21.0" "@babel/highlight@^7.10.4", "@babel/highlight@^7.18.6": version "7.18.6" @@ -389,10 +393,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.10.3", "@babel/parser@^7.12.11", "@babel/parser@^7.12.7", "@babel/parser@^7.14.7", "@babel/parser@^7.20.13", "@babel/parser@^7.20.15", "@babel/parser@^7.20.7": - version "7.20.15" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.15.tgz#eec9f36d8eaf0948bb88c87a46784b5ee9fd0c89" - integrity sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg== +"@babel/parser@^7.1.0", "@babel/parser@^7.10.3", "@babel/parser@^7.12.11", "@babel/parser@^7.12.7", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.0", "@babel/parser@^7.21.1", "@babel/parser@^7.21.2": + version "7.21.2" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.2.tgz#dacafadfc6d7654c3051a66d6fe55b6cb2f2a0b3" + integrity sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ== "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": version "7.18.6" @@ -532,10 +536,10 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/plugin-proposal-optional-chaining@^7.12.7", "@babel/plugin-proposal-optional-chaining@^7.18.9", "@babel/plugin-proposal-optional-chaining@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.20.7.tgz#49f2b372519ab31728cc14115bb0998b15bfda55" - integrity sha512-T+A7b1kfjtRM51ssoOfS1+wbyCVqorfyZhT99TvxxLMirPShD8CzKMRepMlCBGM5RpHMbn8s+5MMHnPstJH6mQ== +"@babel/plugin-proposal-optional-chaining@^7.12.7", "@babel/plugin-proposal-optional-chaining@^7.18.9", "@babel/plugin-proposal-optional-chaining@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz#886f5c8978deb7d30f678b2e24346b287234d3ea" + integrity sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA== dependencies: "@babel/helper-plugin-utils" "^7.20.2" "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" @@ -968,13 +972,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-runtime@^7.19.6": - version "7.19.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.6.tgz#9d2a9dbf4e12644d6f46e5e75bfbf02b5d6e9194" - integrity sha512-PRH37lz4JU156lYFW1p8OxE5i7d6Sl/zV58ooyr+q1J1lnQPyg5tIiXlIwNVhJaY4W3TmOtdc8jqdXQcB1v5Yw== +"@babel/plugin-transform-runtime@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.21.0.tgz#2a884f29556d0a68cd3d152dcc9e6c71dfb6eee8" + integrity sha512-ReY6pxwSzEU0b3r2/T/VhqMKg/AkceBT19X0UptA3/tYi5Pe2eXgEUH+NNMC5nok6c6XQz5tyVTUpuezRfSMSg== dependencies: "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-plugin-utils" "^7.20.2" babel-plugin-polyfill-corejs2 "^0.3.3" babel-plugin-polyfill-corejs3 "^0.6.0" babel-plugin-polyfill-regenerator "^0.4.1" @@ -1016,13 +1020,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.9" -"@babel/plugin-transform-typescript@^7.18.6": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.20.0.tgz#2c7ec62b8bfc21482f3748789ba294a46a375169" - integrity sha512-xOAsAFaun3t9hCwZ13Qe7gq423UgMZ6zAgmLxeGGapFqlT/X3L5qT2btjiVLlFn7gWtMaVyceS5VxGAuKbgizw== +"@babel/plugin-transform-typescript@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.0.tgz#f0956a153679e3b377ae5b7f0143427151e4c848" + integrity sha512-xo///XTPp3mDzTtrqXoBlK9eiAYW3wv9JXglcn/u1bi60RW11dEUxIgA8cbnDhutS1zacjMRmAwxE0gMklLnZg== dependencies: - "@babel/helper-create-class-features-plugin" "^7.19.0" - "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-create-class-features-plugin" "^7.21.0" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-typescript" "^7.20.0" "@babel/plugin-transform-unicode-escapes@^7.18.10": @@ -1153,19 +1157,19 @@ "@babel/plugin-transform-react-jsx-development" "^7.18.6" "@babel/plugin-transform-react-pure-annotations" "^7.18.6" -"@babel/preset-typescript@^7.12.7", "@babel/preset-typescript@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz#ce64be3e63eddc44240c6358daefac17b3186399" - integrity sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ== +"@babel/preset-typescript@^7.12.7", "@babel/preset-typescript@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.21.0.tgz#bcbbca513e8213691fe5d4b23d9251e01f00ebff" + integrity sha512-myc9mpoVA5m1rF8K8DgLEatOYFDpwC+RkMkjZ0Du6uI62YvDe8uxIEYVs/VCdSJ097nlALiU/yBC7//3nI+hNg== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-validator-option" "^7.18.6" - "@babel/plugin-transform-typescript" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-validator-option" "^7.21.0" + "@babel/plugin-transform-typescript" "^7.21.0" -"@babel/register@^7.12.1", "@babel/register@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.18.9.tgz#1888b24bc28d5cc41c412feb015e9ff6b96e439c" - integrity sha512-ZlbnXDcNYHMR25ITwwNKT88JiaukkdVj/nG7r3wnuXkOTHc60Uy05PwMCPre0hSkY68E6zK3xz+vUJSP2jWmcw== +"@babel/register@^7.12.1", "@babel/register@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.21.0.tgz#c97bf56c2472e063774f31d344c592ebdcefa132" + integrity sha512-9nKsPmYDi5DidAqJaQooxIhsLJiNMkGr8ypQ8Uic7cIox7UCDsM7HuUGxdGT7mSDTYbqzIdsOWzfBton/YJrMw== dependencies: clone-deep "^4.0.1" find-cache-dir "^2.0.0" @@ -1181,10 +1185,10 @@ core-js-pure "^3.25.1" regenerator-runtime "^0.13.10" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.17.8", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.13", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": - version "7.20.13" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.13.tgz#7055ab8a7cff2b8f6058bf6ae45ff84ad2aded4b" - integrity sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA== +"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.17.8", "@babel/runtime@^7.18.3", "@babel/runtime@^7.21.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673" + integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw== dependencies: regenerator-runtime "^0.13.11" @@ -1197,26 +1201,26 @@ "@babel/parser" "^7.20.7" "@babel/types" "^7.20.7" -"@babel/traverse@^7.10.3", "@babel/traverse@^7.12.11", "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.19.0", "@babel/traverse@^7.19.1", "@babel/traverse@^7.20.10", "@babel/traverse@^7.20.12", "@babel/traverse@^7.20.13", "@babel/traverse@^7.20.7", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.2": - version "7.20.13" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.13.tgz#817c1ba13d11accca89478bd5481b2d168d07473" - integrity sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ== +"@babel/traverse@^7.10.3", "@babel/traverse@^7.12.11", "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.19.0", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.2": + version "7.21.2" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.2.tgz#ac7e1f27658750892e815e60ae90f382a46d8e75" + integrity sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw== dependencies: "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.7" + "@babel/generator" "^7.21.1" "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" + "@babel/helper-function-name" "^7.21.0" "@babel/helper-hoist-variables" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.20.13" - "@babel/types" "^7.20.7" + "@babel/parser" "^7.21.2" + "@babel/types" "^7.21.2" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.10.3", "@babel/types@^7.12.11", "@babel/types@^7.12.7", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.7.tgz#54ec75e252318423fc07fb644dc6a58a64c09b7f" - integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg== +"@babel/types@^7.0.0", "@babel/types@^7.10.3", "@babel/types@^7.12.11", "@babel/types@^7.12.7", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": + version "7.21.2" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.2.tgz#92246f6e00f91755893c2876ad653db70c8310d1" + integrity sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw== dependencies: "@babel/helper-string-parser" "^7.19.4" "@babel/helper-validator-identifier" "^7.19.1" @@ -1539,10 +1543,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@75.1.0": - version "75.1.0" - resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-75.1.0.tgz#6bdb2a12e5dd503258e74d5585803f52b826b83e" - integrity sha512-HJgoARNsXeYDIGO9sKV+wwfmFA2IKL9hjOMj8B0PZ4fA6Euprw7KPLkakUbwjTCm0rqYUf/6zmXRafvzvdKLmA== +"@elastic/eui@75.1.2": + version "75.1.2" + resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-75.1.2.tgz#c8ccb1728162b131e49a16833468ab2b0228f1bf" + integrity sha512-J6u16NR3BD5snje2CSWnk+JvEQ7y/8tzpmi2Ul+WWfzQwvf7DsKtouSIs91jdzC1QGSN26S1D3wKZvzaszXacg== dependencies: "@types/chroma-js" "^2.0.0" "@types/lodash" "^4.14.160" @@ -2680,6 +2684,14 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" +"@jridgewell/gen-mapping@^0.1.0": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" + integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== + dependencies: + "@jridgewell/set-array" "^1.0.0" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": version "0.3.2" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" @@ -2689,15 +2701,15 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/resolve-uri@^3.0.3": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz#68eb521368db76d040a6315cdb24bf2483037b9c" - integrity sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew== +"@jridgewell/resolve-uri@3.1.0", "@jridgewell/resolve-uri@^3.0.3": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" + integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== -"@jridgewell/set-array@^1.0.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.1.tgz#36a6acc93987adcf0ba50c66908bd0b70de8afea" - integrity sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ== +"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== "@jridgewell/source-map@^0.3.2": version "0.3.2" @@ -2707,10 +2719,10 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.11" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec" - integrity sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg== +"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.14" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== "@jridgewell/trace-mapping@0.3.9": version "0.3.9" @@ -2720,13 +2732,13 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.0", "@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.14", "@jridgewell/trace-mapping@^0.3.15", "@jridgewell/trace-mapping@^0.3.8", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.15" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz#aba35c48a38d3fd84b37e66c9c0423f9744f9774" - integrity sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g== +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.14", "@jridgewell/trace-mapping@^0.3.15", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.17" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" + integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" "@jsdevtools/ono@^7.1.3": version "7.1.3" @@ -2785,6 +2797,10 @@ version "0.0.0" uid "" +"@kbn/alerts-as-data-utils@link:packages/kbn-alerts-as-data-utils": + version "0.0.0" + uid "" + "@kbn/alerts-restricted-fixtures-plugin@link:x-pack/test/alerting_api_integration/common/plugins/alerts_restricted": version "0.0.0" uid "" @@ -3041,6 +3057,10 @@ version "0.0.0" uid "" +"@kbn/content-management-examples-plugin@link:examples/content_management_examples": + version "0.0.0" + uid "" + "@kbn/content-management-plugin@link:src/plugins/content_management": version "0.0.0" uid "" @@ -4057,6 +4077,10 @@ version "0.0.0" uid "" +"@kbn/expandable-flyout@link:packages/kbn-expandable-flyout": + version "0.0.0" + uid "" + "@kbn/expect@link:packages/kbn-expect": version "0.0.0" uid "" @@ -12923,10 +12947,10 @@ cypress-recurse@^1.27.0: dependencies: humanize-duration "^3.27.3" -cypress@^12.5.1: - version "12.5.1" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-12.5.1.tgz#effdcccdd5a6187d61d497300903d4f3b5b21b6e" - integrity sha512-ZmCmJ3lsyeOpBfh410m5+AO2CO1AxAzFBt7k6/uVbNcrNZje1vdiwYTpj2ksPKg9mjr9lR6V8tmlDNMvr4H/YQ== +cypress@^12.6.0: + version "12.6.0" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-12.6.0.tgz#d71a82639756173c0682b3d467eb9f0523460e91" + integrity sha512-WdHSVaS1lumSd5XpVTslZd8ui9GIGphrzvXq9+3DtVhqjRZC5M70gu5SW/Y/SLPq3D1wiXGZoHC6HJ7ESVE2lw== dependencies: "@cypress/request" "^2.88.10" "@cypress/xvfb" "^1.2.4" @@ -12945,7 +12969,7 @@ cypress@^12.5.1: commander "^5.1.0" common-tags "^1.8.0" dayjs "^1.10.4" - debug "^4.3.2" + debug "^4.3.4" enquirer "^2.3.6" eventemitter2 "6.4.7" execa "4.1.0" @@ -17569,9 +17593,9 @@ inquirer@^8.2.3: wrap-ansi "^7.0.0" install-artifact-from-github@^1.3.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/install-artifact-from-github/-/install-artifact-from-github-1.3.1.tgz#eefaad9af35d632e5d912ad1569c1de38c3c2462" - integrity sha512-3l3Bymg2eKDsN5wQuMfgGEj2x6l5MCAv0zPL6rxHESufFVlEAKW/6oY9F1aGgvY/EgWm5+eWGRjINveL4X7Hgg== + version "1.3.2" + resolved "https://registry.yarnpkg.com/install-artifact-from-github/-/install-artifact-from-github-1.3.2.tgz#1a16d9508e40330523a3017ae0d4713ccc64de82" + integrity sha512-yCFcLvqk0yQdxx0uJz4t9Z3adDMLAYrcGYv546uRXCSvxE+GqNYhhz/KmrGcUKGI/gVLR9n/e/zM9jX/+ASMJQ== internal-slot@^1.0.3: version "1.0.3" @@ -25489,10 +25513,10 @@ select-hose@^2.0.0: resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo= -selenium-webdriver@^4.8.0: - version "4.8.0" - resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-4.8.0.tgz#386d57f23fe8edf5178f5bd06aae9ffaffbcb692" - integrity sha512-s/HL8WNwy1ggHR244+tAhjhyKMJnZLt1HKJ6Gn7nQgVjB/ybDF+46Uui0qI2J7AjPNJzlUmTncdC/jg/kKkn0A== +selenium-webdriver@^4.8.1: + version "4.8.1" + resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-4.8.1.tgz#4b0a546c4ea747c44e9688c108f7a46b8d8244ab" + integrity sha512-p4MtfhCQdcV6xxkS7eI0tQN6+WNReRULLCAuT4RDGkrjfObBNXMJ3WT8XdK+aXTr5nnBKuh+PxIevM0EjJgkxA== dependencies: jszip "^3.10.0" tmp "^0.2.1" @@ -27102,10 +27126,10 @@ terser@^4.1.2, terser@^4.6.3: source-map "~0.6.1" source-map-support "~0.5.12" -terser@^5.14.1, terser@^5.16.3, terser@^5.3.4, terser@^5.9.0: - version "5.16.3" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.3.tgz#3266017a9b682edfe019b8ecddd2abaae7b39c6b" - integrity sha512-v8wWLaS/xt3nE9dgKEWhNUFP6q4kngO5B8eYFUuebsu7Dw/UNAnpUod6UHo04jSSkv8TzKHjZDSd7EXdDQAl8Q== +terser@^5.14.1, terser@^5.16.4, terser@^5.3.4, terser@^5.9.0: + version "5.16.5" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.5.tgz#1c285ca0655f467f92af1bbab46ab72d1cb08e5a" + integrity sha512-qcwfg4+RZa3YvlFh0qjifnzBHjKGNbtDo9yivMqMFDy9Q6FSaQWSB/j1xKhsoUFJIqDOM3TsN6D5xbrMrFcHbg== dependencies: "@jridgewell/source-map" "^0.3.2" acorn "^8.5.0"